EF Core 配置方式,配置数据模型,数据种子

科技资讯 投稿 51700 0 评论

EF Core 配置方式,配置数据模型,数据种子

第二节描述两种配置方式,即:数据注释(data annotations)和 Fluent API 方式。

1 概述

数据实体(Entity)的类名、属性等,称之为约定(conventions),约定主要是为了定义数据模型(Model)的形状。

EF Core官方文档:创建并配置模型。

2 配置方式

2.1 数据注释(data annotations)

[Table("Blogs"]
public class Blog
{
    public int BlogId { get; set; }

    [Required]
    public string Url { get; set; }
}

注意:数据注释的方式的优先级高于约定(conventions)但低于 Fluent API,即数据注释的方式会被 Fluent API 覆盖。

2.2 Fluent API

通过在派生的 DbContext 中,重写 OnModelCreating 方法,并使用 ModelBuilder API 来配置模型。

internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder
    {
        // 写法1:链式配置
        modelBuilder.Entity<Blog>(
            .ToTable("Blogs"
            .Property(b => b.Url.IsRequired(;
        
        // 写法2:委托函数配置
        modelBuilder.Entity<Post>(eb => 
        {
            eb.ToTable("Posts";
            eb.Property(b => b.Title.IsRequired(;
        };
    }
}

2.2.1 分组配置

可以实现类似于批量配置,具体请参考分组配置。

modelBuilder.ApplyConfigurationsFromAssembly(typeof(BlogEntityTypeConfiguration.Assembly;

注意:应用配置的顺序是不确定的,因此仅当顺序不重要时才应使用此方法。

3 配置数据模型

3.1 在模型中包含类型

3.1.1 迁移时,创建表的情况

使用 EF Core 添加迁移时,哪些实体会被创建表呢?包含以下三种情况:

  1. 在 DbContext 的 DbSet 属性中公开的实体类

  2. 在 DbContext 的 OnModelCreating 方法中指定的实体类

  3. 以上两种情况的实体类内,通过递归探索导航属性发现的实体类

下面的代码示例中,数据模型中包含的实体类有:

  • 包含 ,因为它在上下文的 DbSet 属性中公开。

  • 包含 ,因为它是通过  导航属性发现的。

  • 包含 因为它是  中指定的。

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; }
}

3.2 配置实体类型(Entity types)

3.2.1 数据注释(data annotations)

//从模型(model)中排除类型(class)
[NotMapped]

//指定表名称
[Table("blogs"]
//指定架构(scheme)
[Table("blogs", Schema = "blogging"]

//表注释
[Comment("Blogs managed on the website"]

3.2.2 Fluent API

//从模型(model)中排除类型(class)
modelBuilder.Ignore<BlogMetadata>(;
//从迁移中排除,生成迁移将不会包含 表AspNetUsers,但 IdentityUser 仍在模型中
modelBuilder.Entity<IdentityUser>(.ToTable("AspNetUsers", t => t.ExcludeFromMigrations(;

//指定表名称
modelBuilder.Entity<Blog>(.ToTable("blogs";
//指定架构(scheme)
modelBuilder.Entity<Blog>(.ToTable("blogs", schema: "blogging";
//通用配置:默认架构名
modelBuilder.HasDefaultSchema("blogging";

//视图映射
//映射到视图将删除默认表映射,但从 EF 5.0 开始,实体类型也可以显式映射到表。 在这种情况下,查询映射将用于查询,表映射将用于更新。
modelBuilder.Entity<Blog>(.ToView("blogsView", schema: "blogging";

//表注释
modelBuilder.Entity<Blog>(.HasComment("Blogs managed on the website";

//表值函数映射
modelBuilder.Entity<BlogWithMultiplePosts>(.HasNoKey(.ToFunction("BlogsWithMultiplePosts";

//共享类型实体(Shared-type entity types)
//不理解
//https://docs.microsoft.com/zh-cn/ef/core/modeling/entity-types?tabs=data-annotations#shared-type-entity-types

3.3 配置实体属性(Entity properties)

3.3.1 数据注释(data annotations)

//排除属性
[NotMapped]
//备注
[Comment("The URL of the blog"]

[Column("blog_id"]
[Column(TypeName = "varchar(200"]
[MaxLength(500]

//精度和小数位
[Precision(14, 2]
public decimal Score { get; set; }
[Precision(3]
public DateTime LastUpdated { get; set; }

//nvarchar 表示 Unicode 数据,varchar 表示非 Unicode 数据
[Unicode(false]
[Required]

//列排序
//默认情况下,在使用迁移创建表时,EF Core 首先为主键列排序,然后为实体类型和从属类型的属性排序,最后为基类型中的属性排序。(顺序:主键 >> 属性 >> 基类属性
//在一般情况下,大多数数据库仅支持在创建表时对列进行排序。 这意味着不能使用列顺序特性对现有表中的列进行重新排序。
[Column(Order = 0]
[Column(Order = 1]

3.3.2 Fluent API

//排除属性
modelBuilder.Entity<Blog>(.Ignore(b => b.LoadedFromDatabase;
//备注
modelBuilder.Entity<Blog>(.Property(b => b.Url.HasComment("The URL of the blog";

modelBuilder.Entity<Blog>(.Property(b => b.BlogId.HasColumnName("blog_id";
modelBuilder.Entity<Blog>(.Property(b => b.Url.HasColumnType("varchar(200";
modelBuilder.Entity<Blog>(.Property(b => b.Url.HasMaxLength(500;

//精度和小数位
modelBuilder.Entity<Blog>(.Property(b => b.Score.HasPrecision(14, 2;
modelBuilder.Entity<Blog>(.Property(b => b.LastUpdated.HasPrecision(3;

//nvarchar 表示 Unicode 数据,varchar 表示非 Unicode 数据
modelBuilder.Entity<Book>(.Property(b => b.Isbn.IsUnicode(false;
modelBuilder.Entity<Blog>(.Property(b => b.Url.IsRequired(;

//可以定义文本列的排序规则,以确定如何比较和排序。 
//排序规则:https://docs.microsoft.com/zh-cn/ef/core/miscellaneous/collations-and-case-sensitivity
//例如,以下代码片段将 SQL Server 列配置为不区分大小写
modelBuilder.Entity<Customer>(.Property(c => c.Name
    .UseCollation("SQL_Latin1_General_CP1_CI_AS";

modelBuilder.Entity<Employee>(.Property(b => b.Id.HasColumnOrder(0;
modelBuilder.Entity<Employee>(.Property(b => b.FirstName.HasColumnOrder(1;

3.4 配置主键、外键、索引

3.4.1 数据注释

//主键
[Key]
//无主键
[Keyless]

//外键
[ForeignKey]
//反向属性:https://docs.microsoft.com/zh-cn/ef/core/modeling/relationships?tabs=data-annotations%2Cfluent-api-simple-key%2Csimple-key#manual-configuration
[InverseProperty("Author"]

//索引
[Index(nameof(Url]
public class Blog 
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}
//复合索引
[Index(nameof(FirstName, nameof(LastName]
//唯一索引
[Index(nameof(Url, IsUnique = true]
//索引名称
[Index(nameof(Url, Name = "Index_Url"]

3.4.2 Fluent API

//主键
modelBuilder.Entity<Car>(.HasKey(c => c.LicensePlate;
//复合主键
modelBuilder.Entity<Car>(.HasKey(c => new { c.State, c.LicensePlate };
//无主键
modelBuilder.Entity<BlogPostsCount>(.HasNoKey(;

//备选键:https://docs.microsoft.com/zh-cn/ef/core/modeling/keys?tabs=fluent-api#alternate-keys
//备选键 HasPrincipalKey
modelBuilder.Entity<Post>(
            .HasOne(p => p.Blog
            .WithMany(b => b.Posts
            .HasForeignKey(p => p.BlogUrl
            .HasPrincipalKey(b => b.Url;
//将单个属性配置为备选键
modelBuilder.Entity<Car>(
        .HasAlternateKey(c => c.LicensePlate;
//复合备选键
modelBuilder.Entity<Car>(
        .HasAlternateKey(c => new { c.State, c.LicensePlate };
//配置备选键的索引和唯一约束的名称
modelBuilder.Entity<Car>(
        .HasAlternateKey(c => c.LicensePlate
        .HasName("AlternateKey_LicensePlate";

//配置关系
modelBuilder.Entity<Post>(
    		.HasOne(p => p.Blog
    		.WithMany(b => b.Posts;
//配置外键
modelBuilder.Entity<Post>(
            .HasOne(p => p.Blog
            .WithMany(b => b.Posts
            .HasForeignKey(p => p.BlogForeignKey;
//配置外键约束的名称
modelBuilder.Entity<Post>(
        .HasOne(p => p.Blog
        .WithMany(b => b.Posts
        .HasForeignKey(p => p.BlogId
        .HasConstraintName("ForeignKey_Post_Blog";
//关系:https://docs.microsoft.com/zh-cn/ef/core/modeling/relationships

//索引
modelBuilder.Entity<Blog>(.HasIndex(b => b.Url;
modelBuilder.Entity<Person>(.HasIndex(p => new { p.FirstName, p.LastName };
//唯一索引
modelBuilder.Entity<Blog>(.HasIndex(b => b.Url.IsUnique(;
//索引名称
modelBuilder.Entity<Blog>(.HasIndex(b => b.Url.HasDatabaseName("Index_Url";
//索引筛选器:https://docs.microsoft.com/zh-cn/ef/core/modeling/indexes?tabs=fluent-api#index-filter
modelBuilder.Entity<Blog>(.HasIndex(b => b.Url.HasFilter("[Url] IS NOT NULL";
//包含列:SQL Server 的 Include 关键字
modelBuilder.Entity<Post>(.HasIndex(p => p.Url
        .IncludeProperties(p => new { p.Title, p.PublishedOn };
//检查约束
modelBuilder.Entity<Product>(.HasCheckConstraint("CK_Prices", "[Price] > [DiscountedPrice]", c => c.HasName("CK_Product_Prices";

3.5 值转换(value conversions)

3.5.1 基本配置

假设将一个枚举和实体类型定义为:

public class Rider
{
    public int Id { get; set; }
    public EquineBeast Mount { get; set; }
}

public enum EquineBeast
{
    Donkey,
    Mule,
    Horse,
    Unicorn
}

可以将枚举值(如字符串 "Donkey"、"Mule"等)存储在数据库中。

protected override void OnModelCreating(ModelBuilder modelBuilder
{
    modelBuilder
        .Entity<Rider>(
        .Property(e => e.Mount
        .HasConversion(
            v => v.ToString(,
            v => (EquineBeastEnum.Parse(typeof(EquineBeast, v;
}

3.5.2 批量配置

public class CurrencyConverter : ValueConverter<Currency, decimal>
{
    public CurrencyConverter(
        : base(
            v => v.Amount,
            v => new Currency(v
    {
    }
}

配置

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder
{
    configurationBuilder
        .Properties<Currency>(
        .HaveConversion<CurrencyConverter>(;
}

3.5.3 ValueConverter 类

protected override void OnModelCreating(ModelBuilder modelBuilder
{
    var converter = new ValueConverter<EquineBeast, string>(
        v => v.ToString(,
        v => (EquineBeastEnum.Parse(typeof(EquineBeast, v;

    modelBuilder
        .Entity<Rider>(
        .Property(e => e.Mount
        .HasConversion(converter;
}

3.5.4 内置转换器

下面是一个示例,更多的请翻查官方文档:内置转换器

protected override void OnModelCreating(ModelBuilder modelBuilder
{
    modelBuilder
        .Entity<Rider>(
        .Property(e => e.Mount
        .HasConversion<string>(;
}

4 数据种子(data seeding)

在  中配置种子数据:

modelBuilder.Entity<Blog>(.HasData(new Blog { BlogId = 1, Url = "http://sample.com" };

匿名对象:

modelBuilder.Entity<Post>(.HasData(
    new { BlogId = 1, PostId = 2, Title = "Second post", Content = "Test 2" };

多行数据

modelBuilder.Entity<Post>(.OwnsOne(p => p.AuthorName.HasData(
    new { PostId = 1, First = "Andriy", Last = "Svyryd" },
    new { PostId = 2, First = "Diego", Last = "Vega" };

编程笔记 » EF Core 配置方式,配置数据模型,数据种子

赞同 (64) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽