创建并配置模型
EF Core 使用元数据模型来描述如何将应用程序的实体类型映射到基础数据库。 此模型是使用一组约定构建的,这些约定是寻找常见模式的启发式方法。 然后,可以使用映射特性(也称为数据注释)自定义模型和/或在 OnModelCreating 中调用 ModelBuilder 方法(也称为 Fluent API),这两者都将替代约定执行的配置。
大多数配置可以应用于面向任何数据存储的模型。 提供程序还可以启用特定于特定数据存储的配置,也可以忽略不支持或不适用的配置。 有关提供程序特定配置的文档,请参阅数据库提供程序部分。
提示
可在 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
与其显式调用 Configure
,不如改为在实体类型上放置 EntityTypeConfigurationAttribute,以便 EF Core 可以查找并使用适当的配置。 例如:
[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 未立即识别的模式应用配置。
删除现有约定
有时,其中一个内置约定可能不适用于你的应用程序,在这种情况下,可以将其删除。
提示
如果模型不使用映射特性(又名数据注释)进行配置,则可以安全地删除名称以 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));