EFCore学习笔记(2)——实体类型

19 篇文章 1 订阅

一、实体类型(Entity Types)

学了一段时间EF Core后,我觉得我对EF Core的理解可以用一张图来描述,于是简单画了一下这张图:
在这里插入图片描述

EF Core中涉及到的有些概念可以结合这张图来理解。
OK,进入正文,继续学习。
上下文(Context的派生类)中包含一种实体类型的DbSet表示该实体存在于EF Core的模型中;

internal class MyContext : DbContext
{
	// Blog是实体
    public DbSet<Blog> Blogs { get; set; }
	...
}

通常称这样的类型为实体(Entity)(所以实体就是那个类,而不是DbSet<T> xxx)。EF Core能从数据库中读数据至实体的实例或将实体实例写入数据库,并且如果你用的是关系数据库,EF Core能通过migrations为你的实体建表。

1. 在model中包含类型

按照约定(指的是EF Core这套技术设计/使用上的规定),在你定义的上下文中的DbSet属性暴露的类型会作为实体被包含在model中。

在OnModelCreating方法中指定的实体类型也会被包含在内,并且由递归探索到的实体类型的导航属性所找到的任何类型也会被包含在内(很拗口,结合下面例子进行理解)。

在下面代码示例中,包含了上面描述的所有类型:

  • Blog是被包含在内的,因为它在上下文中由DbSet属性暴露;
  • Post是被包含在内的,因为它通过Blog.Posts导航属性可以发现;
  • AudiEntry是,因为它在OnModelCreating中被指定。
internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<AuditEntry>();
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog { get; set; }
}

public class AuditEntry
{
    public int AuditEntryId { get; set; }
    public string Username { get; set; }
    public string Action { get; set; }
}

2. 从model中排除类型

如果你不想某个类型被包含到模型中,你可以排除它:

// 两种方式
// 1. 数据标注方式
[NotMapped]
public class BlogMetadata
{
    public DateTime LoadedFromDatabase { get; set; }
}
// 2. Fluent API方式
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
	modelBuilder.Ignore<BlogMetadata>();
}

△2.1. 从migrations中排除

注意:
该功能在EF Core 5.0之后才引入的。

虽然这个功能我暂时不用,不过还是简单学习下,留个印象。
有时,将相同的实体类型映射到多个DbContext中是很有用的。尤其在使用有界上下文时,因为每个有界上下文通常有不同的DbContext类型。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<IdentityUser>()
        .ToTable("AspNetUsers", t => t.ExcludeFromMigrations());
}

通过这个配置项,migrations将不会创建AspNetUsers表,但IdentityUser仍然会包含在模型中,且能被正常使用。
如果你需要再次使用migrations来管理表,那么应该在不排除AspNetUsers的情况下创建一个新的migration。现在,下一个migration将包含对表做的任何更改。

3. 表名

按照约定,每个实体类型将被设置为映射到与暴露实体的DbSet属性有着相同名称的数据表。若给定的实体不存在DbSet,则使用类名。
你可以手动配置表名:

// 两种方式
// 1. 数据标注
[Table("blogs")]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}
// 2. Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .ToTable("blogs");
}

4. Table schema

schema这个概念我也是不太熟悉,所以特地去补了一下。
因为在我的感觉中,数据库的关系是这样的, 数据服务器下面有数据库,数据库下面有数据表,表中有字段,仅此而已。
实际上当我打开MySQL workbench,打开一个数据服务器连接后,展现的树状图如下,

在这里插入图片描述

显然,它们都是在一个叫SCHEMAS的根节点下的。所以这个图标是数据库形状的子节点居然是schema?!
这边我也不想展开讲,网上说法很多,但是就目前来看,在MySQL中把schema理解成database没有大问题。

当使用关系数据库时,表按照约定会创建到你数据库中默认的table schema中。例如,SQL Server将会使用dbo schema(SQLite不支持schemas)。
你可以按照下面方式在指定的schema中配置创建的表:

// 1. 数据标注
[Table("blogs", Schema = "blogging")]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}
// 2. Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .ToTable("blogs", schema: "blogging");
}

除了为每个表指定schema,你也可以使用fluent API在模型上定义默认的schema:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.HasDefaultSchema("blogging");
}
注意:设置默认的schema还会影响其他的数据库对象,例如序列(sequence)。

△5. 视图映射

实体类型能通过Fluent API映射到到数据库视图中。

注意:
EF会假设所引用的视图已经存在于数据库中,它不会在migration中自动创建它。
modelBuilder.Entity<Blog>()
    .ToView("blogsView", schema: "blogging");

映射到视图会删除默认的表映射,但是从EF 5.0开始,实体类也可以显示映射到表。在这种情况下,查询映射将用于查询,表映射将用于更新。

提示:
要测试使用内存提供程序映射到视图的无键实体类型,通过ToInMemoryQuery将它们映射到查询。

△6. 表值的函数映射

有时也会将实体类型映射到表值函数(TVF),而不是数据库中的表。为了说明这点,我们定义另一个实体,它表示带有多个帖子的blog。在本例中,实体是无键的,但它也不是必须的。

public class BlogWithMultiplePosts
{
    public string Url { get; set; }
    public int PostCount { get; set; }
}

接着,在数据库中创建以下表值函数,它只返回带有多个帖子的博客,以及与每个博客相关的帖子数量:

CREATE FUNCTION dbo.BlogsWithMultiplePosts()
RETURNS TABLE
AS
RETURN
(
    SELECT b.Url, COUNT(p.BlogId) AS PostCount
    FROM Blogs AS b
    JOIN Posts AS p ON b.BlogId = p.BlogId
    GROUP BY b.BlogId, b.Url
    HAVING COUNT(p.BlogId) > 1
)

现在,BlogWithMultiplePosts实体能以以下方式映射到函数中:

modelBuilder.Entity<BlogWithMultiplePosts>().HasNoKey().ToFunction("BlogsWithMultiplePosts");
注意:
为了映射实体至表值函数,该函数必须是无参的。

通常,实体属性将映射到由TVF返回的匹配列。若TVF返回的列有与entity属性不同的名称,那么可以用HasColumnName方法配置实体的列,就像映射到普通包时一样。

当实体类映射到表值函数时,查询如下:

var query = from b in context.Set<BlogWithMultiplePosts>()
            where b.PostCount > 3
            select new { b.Url, b.PostCount };

生成以下SQL:

SELECT [b].[Url], [b].[PostCount]
FROM [dbo].[BlogsWithMultiplePosts]() AS [b]
WHERE [b].[PostCount] > 3

△7. 表备注

你可以在数据表上设置任意文本注释,允许你在数据库中记录你的模式:

// 1. 数据标注 EF Core 5.0引入的
[Comment("Blogs managed on the website")]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}
// 2. fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasComment("Blogs managed on the website");
}

△8. 共享类型的实体类型

注意:
EF Core 5.0引入了支持共享类型的实体类型。

使用相同CLR类型的实体类型被称为共享类型的实体类型。这些实体类型需要配置一个唯一的名称,除了CLR类型,每当使用共享类型实体类型时,都必须提供这个名称。这意味着相应的DbSet属性必须使用Set调用实现。

internal class MyContext : DbContext
{
    public DbSet<Dictionary<string, object>> Blogs => Set<Dictionary<string, object>>("Blog");

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.SharedTypeEntity<Dictionary<string, object>>(
            "Blog", bb =>
            {
                bb.Property<int>("BlogId");
                bb.Property<string>("Url");
                bb.Property<DateTime>("LastUpdated");
            });
    }
}

二、小结

本意是稍微学习一下实体类型是啥,以及实体类型在模型中扮演怎样的角色。结果词条太多,写了这么多玩意儿。于是我用 标注了我认为对初学者来说暂时不那么重要的条目。

最后用自己的语言总结一下实体类型的特点:

  • 实体是个类,它是用来与数据库通信的,它本身不能起通信作用,需要以DbSet<T>来在上下文中暴露才行(还有其它两种方式)
  • 每个实体类型将被设置为映射到与暴露实体的DbSet属性有着相同名称的数据表。若给定的实体不存在DbSet,则使用类名。(所以操作实体类时,用它的实例来读写数据,可以认为在操作一张表)
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你好!感谢提问。下面是有关 K210 学习笔记五中串口通信的一些内容: 在 K210 开发板上,串口是一种常见的通信接口,用于将数据从一个设备传输到另一个设备。串口通信通常使用 UART (Universal Asynchronous Receiver/Transmitter) 协议。 要在 K210 上进行串口通信,首先需要使用两个引脚:一个用于接收数据(RX),一个用于发送数据(TX)。这些引脚可以通过 GPIO 端口与外部设备连接。 在 K210 的开发环境中,可以使用 MaixPy 或者 Kendryte Standalone SDK 进行串口编程。 在 MaixPy 中,可以使用 `uart` 模块来进行串口通信。例如,要初始化一个串口对象并设置波特率为 115200,可以使用以下代码: ```python from machine import UART uart = UART(UART.UART1, 115200) ``` 然后,可以使用 `uart.write()` 函数发送数据,使用 `uart.read()` 函数接收数据。例如: ```python uart.write("Hello, world!\n") data = uart.read(10) ``` 在 Kendryte Standalone SDK 中,可以使用 `uart.h` 头文件中的函数来进行串口通信。例如,要初始化一个串口对象并设置波特率为 115200,可以使用以下代码: ```c #include "uart.h" uart_init(UART_DEVICE_1, 115200); ``` 然后,可以使用 `uart_send_data()` 函数发送数据,使用 `uart_receive_data()` 函数接收数据。例如: ```c uart_send_data(UART_DEVICE_1, "Hello, world!\n", 14); char buffer[10]; uart_receive_data(UART_DEVICE_1, buffer, 10); ``` 以上是关于 K210 学习笔记五中串口通信的简要介绍。如果你有更具体的问题,请随时提问!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值