0 前言

本文的第一节,会概述配置模型的作用(对数据模型的补充描述)。

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

第三节开始,主要是将常用的配置记录下来,以便翻查。

1 概述

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

但是光靠约定可能不足以完整描述数据模型,有时我们的数据模型与我们的数据实体可能也有差异,这时,就可以通过数据注释(data annotations)和 Fluent API 补充,具体请参考EF Core官方文档:创建并配置模型

2 配置方式

2.1 数据注释(data annotations)

直接在数据实体上打上对应的标签,如下例子中,标识表名为 Blogs,Url 属性不能为 null

[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

对描述数据模型(Model)具有最高优先级。

通过在派生的 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 在模型中包含类型

在上下文中包含于 DbSet 的类意味着它包含在 EF Core 的模型中;我们通常将这些类称为实体。 EF Core 可以向数据库中读写实体实例,如果使用的是关系数据库,EF Core 可以通过迁移为实体创建表。

3.1.1 迁移时,创建表的情况

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

  1. 在 DbContext 的 DbSet 属性中公开的实体类
  2. 在 DbContext 的 OnModelCreating 方法中指定的实体类
  3. 以上两种情况的实体类内,通过递归探索导航属性发现的实体类

下面是一个官方示例:

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

  • 包含 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; }
}

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 => (EquineBeast)Enum.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 => (EquineBeast)Enum.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)

OnModelCreating 中配置种子数据:

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官方文档:创建模型

EF Core 配置模型的更多相关文章

  1. C# 数据操作系列 - 6 EF Core 配置映射关系

    0. 前言 在<C# 数据操作系列 - 5. EF Core 入门>篇中,我们简单的通过两个类演示了一下EF增删改查等功能.细心的小伙伴可能看了生成的DDL SQL 语句,在里面发现了些端 ...

  2. EF core的模型映射

    在EF core里,可以通过实现IEntityTypeConfiguration来进行映射. 一.官网文档 https://docs.microsoft.com/en-us/ef/core/what- ...

  3. 举例说明EF CORE中模型之间的一对多、多对多关系的实现

    该例子是我临时想出来的,不具有任何的实际意义.类图如图1所示. 图1 类代码: [Table("student")] public class Student { public i ...

  4. DDD Code First 迁移数据实现EF CORE的软删除,值对象迁移配置

    感谢Jeffcky大佬的博客: EntityFramework Core 2.0全局过滤 (HasQueryFilter) https://www.cnblogs.com/CreateMyself/p ...

  5. [翻译 EF Core in Action 1.9] 掀开EF Core的引擎盖看看EF Core内部是如何工作的

    Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...

  6. EF Core 1.0中使用Include的小技巧

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:由于EF Core暂时不支持Lazy Loading,所以利用Include来加载额外 ...

  7. EF Core 2.0 新特性

    前言 目前 EF Core 的最新版本为 2.0.0-priview1-final,所以本篇文章主要是针对此版本的一些说明. 注意:如果你要在Visual Studio 中使用 .NET Core 2 ...

  8. [翻译 EF Core in Action 1.8] MyFirstEfCoreApp应用程序设置

    Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...

  9. ASP.NET Core MVC+EF Core项目实战

    项目背景 本项目参考于<Pro Entity Framework Core 2 for ASP.NET Core MVC>一书,项目内容为party邀请答复. 新建项目 本项目开发工具为V ...

随机推荐

  1. js技术之拖动table标签

    一.js技术之拖动table标签 起因:前几天公司,突然安排一个任务 任务描述:要求尺码table列表要像Excel表中一样可以直接移动整行尺码到任意行位置 技术点:采用ui的sortable技术来h ...

  2. 运筹学之"连通图"和"最小枝杈树"和"最短路线问题"

    一.连通图 必须每个点都有关系 图1 不算连通图 图2含有圈v1,v2,v5,可优化 图3就是所需的连通图 注意:图>连通图>树 二.最小枝杈树 获取是所有节点的最小值,只要是连通图就好, ...

  3. glusterfs架构和原理

    分布式存储已经研究很多年,但直到近年来,伴随着谷歌.亚马逊和阿里等互联网公司云计算和大数据应用的兴起,它才大规模应用到工程实践中.如谷歌的分布式文件系统GFS.分布式表格系统google Bigtab ...

  4. CSDN博客步骤:

    在SCDN看到喜欢的文章想转载又嫌一个一个敲太麻烦,干脆直接收藏.但有时候作者把原文章删除或设置为私密文章后又看不了.所以还是转载来的好.这篇博文为快速转载博客的方法,亲测有效,教程如下. 原博客原址 ...

  5. 《深入理解ES6》笔记—— JavaScript中的类class(9)

    ES5中的近类结构 ES5以及之前的版本,没有类的概念,但是聪明的JavaScript开发者,为了实现面向对象,创建了特殊的近类结构. ES5中创建类的方法:新建一个构造函数,定义一个方法并且赋值给构 ...

  6. 反射常用API以及内省机制(代码)

    学习内容: (1)获取构造函数 这里不贴Person类了,不然代码太多太乱了,只给出一些常用API // 创建字节码对象 Class<?> aClass = Class.forName(& ...

  7. 小程序预览图片wx.previewImage

    效果图:  ====>   ==>  代码: <image mode='aspectFill' bindtap='previewImage' data-src='{{dataList ...

  8. linux(Ubuntu)安装python

    Linux下安装python 提前安装一个依赖环境 (1)ubuntu/Debian: sudo apt-get install -y gcc make cmake build-essential l ...

  9. 三个步骤,从零开始快速部署LoRaServer

    2021年11月29日,ITU(国际电信联盟)标准化部门正式批准了LoRa联盟立项的"ITU-T Y.4480 Low power protocolfor wide area wireles ...

  10.  CPUs Intel 925X/915 Chipset (925X主板芯片组)

    这个是2004年的intel产品的设计(主板,主板芯片组,北桥,南桥),结构也比较清晰,主要想看南桥和北桥的设计. 一些英文解释 ECC是一种能够实现"错误检查和纠正"的技术D92 ...