在您的上下文中包含某个类型的 DbSet 意味着它被包含在 EF Core 的模型中,我们通常将这种类型称为实体。 EF Core 可以从/向数据库读取和写入实体实例,如果使用关系数据库,EF Core 可以通过迁移为实体创建表。
在模型中包括类型
按照约定,上下文上的 DbSet 属性中公开的类型作为实体包含在模型中。 方法中指定的 OnModelCreating 实体类型也包括在内,以递归方式探索其他已发现的实体类型的导航属性找到的任何类型也是如此。
在下面的代码示例中,包括所有类型:
-
Blog被包含,因为它在上下文的 DbSet 属性中被公开。 -
Post是包含的,因为它是通过Blog.Posts导航属性发现的。 -
AuditEntry因为它在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; }
}
从模型中排除类型
如果不希望模型中包含一个类型,可以排除它:
[NotMapped]
public class BlogMetadata
{
public DateTime LoadedFromDatabase { get; set; }
}
从迁移中排除
有时,在多个 DbContext 类型中映射相同的实体类型很有用。 在使用 边界上下文时,尤其如此,每个边界上下文通常有不同的 DbContext 类型。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<IdentityUser>()
.ToTable("AspNetUsers", t => t.ExcludeFromMigrations());
}
使用此配置迁移不会创建 AspNetUsers 表,但仍 IdentityUser 包含在模型中,并且可以正常使用。
如果需要开始使用迁移管理表,则应创建新的迁移,其中 AspNetUsers 未被排除。 下一次迁移现在将包含对表所做的任何更改。
表名称
按照约定,每个实体类型将被设置为映射到与公开该实体的 DbSet 属性同名的数据库表。 如果给定实体不存在 DbSet,则使用类名。
可以手动配置表名称:
[Table("blogs")]
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
表架构
使用关系数据库时,表按约定在数据库的默认架构中创建。 例如,Microsoft SQL Server 将使用 dbo 架构(SQLite 不支持架构)。
可以将表配置为在特定架构中创建,如下所示:
[Table("blogs", Schema = "blogging")]
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
还可以使用 Fluent API 在模型级别定义默认架构,而不是为每个表指定架构:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("blogging");
}
请注意,设置默认架构也会影响其他数据库对象,例如序列。
查看映射
可以使用 Fluent API 将实体类型映射到数据库视图。
注释
EF 将假定引用的视图已存在于数据库中,它不会在迁移中自动创建它。
modelBuilder.Entity<Blog>()
.ToView("blogsView", schema: "blogging");
映射到视图将删除默认表映射,但实体类型也可以显式映射到表。 在这种情况下,视图映射将用于查询,表映射将用于更新。
小窍门
若要使用内存提供程序测试映射到视图的无键实体类型,请通过 ToInMemoryQuery 将其映射为查询。 有关详细信息,请参阅 内存中提供程序文档 。
表值函数映射
可以将实体类型映射到表值函数(TVF),而不是数据库中的表。 为了说明这一点,让我们定义另一个实体,该实体代表具有多个文章的博客。 在此示例中,实体 是无键的,但它不必是。
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 返回的列的名称与实体属性不同,则可以使用 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
表注释
可以设置任意文本注释到数据库表上,以便在数据库中记录您的架构。
[Comment("Blogs managed on the website")]
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
共享类型实体类型
使用相同 CLR 类型的实体类型称为共享类型实体类型。 除了 CLR 类型之外,这些实体类型需要配置唯一名称,每当使用共享类型实体类型时都必须提供该名称。 这意味着必须使用Set调用来实现相应的DbSet属性。
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");
});
}
}