EF Core 使用元数据 模型 描述如何将应用程序的实体类型映射到基础数据库。 此模型是使用一组 约定 构建的 - 一套启发式规则,用于查找常见模式。 然后,可以使用映射属性(也称为数据注释)和/或调用ModelBuilder方法(也称为 fluent API)OnModelCreating来自定义模型,这两者都将替代约定执行的配置。
大多数配置都可以应用于面向任何数据存储的模型。 提供程序还可以启用特定于特定数据存储的配置,还可以忽略不受支持或不适用的配置。 有关特定于提供程序的配置的文档,请参阅 “数据库提供程序 ”部分。
小窍门
可以在 GitHub 上查看本文 的示例 。
使用 fluent API 配置模型
可以在您的派生上下文中重写OnModelCreating
方法,并使用Fluent API来配置模型。 这是配置最强大的方法,允许在不修改实体类的情况下指定配置。 Fluent API 配置具有最高优先级,将替代约定和数据注释。 按照调用方法的顺序应用配置,如果发生任何冲突,则最新调用将替代先前指定的配置。
using Microsoft.EntityFrameworkCore;
namespace EFModeling.EntityProperties.FluentAPI.Required;
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
#region Required
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.IsRequired();
}
#endregion
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
小窍门
若要将相同的配置应用于模型中的多个对象,请参阅 批量配置。
分组配置
为了减小方法的大小 OnModelCreating
,可以将实体类型的所有配置提取到实现 IEntityTypeConfiguration<TEntity>的单独类。
public class BlogEntityTypeConfiguration : IEntityTypeConfiguration<Blog>
{
public void Configure(EntityTypeBuilder<Blog> builder)
{
builder
.Property(b => b.Url)
.IsRequired();
}
}
然后,只需从OnModelCreating
中Configure
调用该方法即可。
new BlogEntityTypeConfiguration().Configure(modelBuilder.Entity<Blog>());
应用程序集中的所有配置
可以应用所有在给定程序集中由实现 IEntityTypeConfiguration
的类型指定的配置。
modelBuilder.ApplyConfigurationsFromAssembly(typeof(BlogEntityTypeConfiguration).Assembly);
注释
将应用配置的顺序是未定义的,因此,仅当顺序无关紧要时,才应使用此方法。
使用 EntityTypeConfigurationAttribute
于实体类型上
可以选择将EntityTypeConfigurationAttribute放置在实体类型上,让 EF Core 能够查找和使用适当的配置,而不是显式调用Configure
。 例如:
[EntityTypeConfiguration(typeof(BookConfiguration))]
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
public string Isbn { get; set; }
}
此属性意味着每当Book
实体类型包含在模型中时,EF Core 都将使用指定的IEntityTypeConfiguration
实现。 实体类型通过常规机制之一包含在模型中。 例如,通过为实体类型创建 DbSet<TEntity> 属性:
public class BooksContext : DbContext
{
public DbSet<Book> Books { get; set; }
//...
或者通过在 OnModelCreating 注册它。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Book>();
}
注释
EntityTypeConfigurationAttribute
类型不会在程序集中自动被发现。 实体类型必须添加到模型中,然后才能在该实体类型上发现该属性。
使用数据注释配置模型
还可以将某些属性(称为 数据注释)应用于类和属性。 数据注释可以覆盖约定,但会被 Fluent API 配置所覆盖。
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace EFModeling.EntityProperties.DataAnnotations.Annotations;
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
}
[Table("Blogs")]
public class Blog
{
public int BlogId { get; set; }
[Required]
public string Url { get; set; }
}
内置约定
EF Core 包含许多默认启用的模型生成约定。 可以在实现 IConvention 接口的类列表中找到所有这些类。 但是,该列表不包括第三方数据库提供程序和插件引入的约定。
应用程序可以删除或替换这些约定中的任何一种,并添加新的 自定义约定 ,这些约定将配置应用于 EF 现成无法识别的模式。
小窍门
下面显示的代码来自 ModelBuildingConventionsSample.cs。
删除现有约定
有时,其中一个内置约定可能不适合应用程序,在这种情况下,可以将其删除。
小窍门
如果模型不使用映射属性(即数据注释)进行配置,则可以安全地删除名称结尾 AttributeConvention
的所有约定,以加快模型生成速度。
示例:不要为外键列创建索引
通常,为外键(FK)列创建索引是有意义的,因此有一个内置的约定: ForeignKeyIndexConvention 查看具有与Blog
和Author
关系的Post
实体类型的模型调试视图时,我们可以看到创建了两个索引——一个用于BlogId
FK,另一个用于AuthorId
FK。
EntityType: Post
Properties:
Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
AuthorId (no field, int?) Shadow FK Index
BlogId (no field, int) Shadow Required FK Index
Navigations:
Author (Author) ToPrincipal Author Inverse: Posts
Blog (Blog) ToPrincipal Blog Inverse: Posts
Keys:
Id PK
Foreign keys:
Post {'AuthorId'} -> Author {'Id'} ToDependent: Posts ToPrincipal: Author ClientSetNull
Post {'BlogId'} -> Blog {'Id'} ToDependent: Posts ToPrincipal: Blog Cascade
Indexes:
AuthorId
BlogId
然而,索引会增加额外负担,并且可能并不总是适当为所有 FK 列创建它们。 若要实现此目的,生成模型时可以删除 ForeignKeyIndexConvention
。
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention));
}
查看模型的 Post
调试视图,可以看到尚未创建 FK 上的索引:
EntityType: Post
Properties:
Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
AuthorId (no field, int?) Shadow FK
BlogId (no field, int) Shadow Required FK
Navigations:
Author (Author) ToPrincipal Author Inverse: Posts
Blog (Blog) ToPrincipal Blog Inverse: Posts
Keys:
Id PK
Foreign keys:
Post {'AuthorId'} -> Author {'Id'} ToDependent: Posts ToPrincipal: Author ClientSetNull
Post {'BlogId'} -> Blog {'Id'} ToDependent: Posts ToPrincipal: Blog Cascade
如果需要,仍可以通过使用IndexAttribute或在OnModelCreating
中进行配置,为外键列显式创建索引。
调试视图
可以在 IDE 的调试器中访问模型生成器调试视图。 例如,使用 Visual Studio:
也可以直接从代码访问它,例如,将调试视图发送到控制台:
Console.WriteLine(context.Model.ToDebugString());
调试视图具有短格式和长格式。 完整格式还包括所有批注,如果您需要查看关系元数据或提供程序特定元数据,这些批注可能很有用。 还可以从代码访问长视图:
Console.WriteLine(context.Model.ToDebugString(MetadataDebugStringOptions.LongDefault));