asp.net core系列 24 EF模型配置(主键,生成值,最大长度,并发标记)
一.主键
键用作每个实体实例的主要唯一标识符。 使用关系数据库时,这会映射到主键的概念。 还可以配置不是主键的唯一标识符。按照约定,名为
Id或
<typename>Id 的属性会配置为实体的键。例如下面二个示例:
class Car { //映射到Car表 Id主键 public string Id { get; set; } } class Car { //映射到Car表CarId主键,约定格式:<typename>Id public string CarId { get; set; } }
除了上面讲到的约定,还可以用数据注释将单个属性配置为实体的键,下面示例使用数据注释配置主键
class Car { //映射到Car表LicensePlate主键 [Key] public string LicensePlate { get; set; } }
还可以使用 Fluent API 将单个属性配置为实体的键。下面示例使用Fluent API配置主键
class MyContext : DbContext { public DbSet<Car> Cars { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Car>().HasKey(c => c.LicensePlate); } } class Car { public string LicensePlate { get; set; } public string Make { get; set; } public string Model { get; set; } }
还可以使用 Fluent API 将多个属性配置为实体的键(称为复合键)。 只能使用 Fluent API 配置复合键 - 不能使用约定来设置复合键,也不能使用数据注释来配置复合键。
class MyContext : DbContext { public DbSet<Car> Cars { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Car>() .HasKey(c => new { c.State, c.LicensePlate }); } }
二.生成的值
对于属性的值生成模式有三种:(1) 无值生成;(2) 在新增时自动生成值;(3) 在添加或更新时自动生成值。下面介绍数据注释中使用DatabaseGeneratedOption枚举来实现,说明这三种生成模式的应用场景:
public class Blog { //没有值生成, 主键一般在数据库中都是自增,所以在EF中不需要给值 [DatabaseGenerated(DatabaseGeneratedOption.None)] public int BlogId { get; set; } //在新增时生成值, 一般插入一条数据时,记录插入的时间 [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public DateTime Inserted { get; set; } //在新增或修改时生成值, 一般记录修改的时间。 [DatabaseGenerated(DatabaseGeneratedOption.Computed)] public DateTime LastUpdated { get; set; } }
除了用数据注释方法生成值,还可以使用Fluent API用于更改某一给定属性的值生成模式。
//没有值生成 modelBuilder.Entity<Blog>().Property(b => b.BlogId).ValueGeneratedNever(); //在新增时生成值 modelBuilder.Entity<Blog>().Property(b => b.Inserted).ValueGeneratedOnAdd(); //在新增或修改时生成值 modelBuilder.Entity<Blog>().Property(b => b.LastUpdated).ValueGeneratedOnAddOrUpdate();
三. 最大长度
最大长度仅适用于数组,数据类型,如
string和
byte[]。将数据传递到提供程序之前,实体框架不会执行任何最大长度验证。 由提供程序或数据存储在适当时机进行验证。以 SQL Server 为目标,超过最大长度将引发异常。
使用数据注释来配置属性的最大长度。 此示例面向 SQL Server,因此使用数据类型 nvarchar(500)。
public class Blog { public int BlogId { get; set; } [MaxLength(500)] public string Url { get; set; } }
使用 Fluent API 配置属性的最大长度。 此示例面向 SQL Server,因此使用数据类型
nvarchar(500)。
modelBuilder.Entity<Blog>().Property(b => b.Url).HasMaxLength(500);
四.并发标记
配置并发标记是用于解决数据库中的并发冲突。数据库并发:指多个进程或用户同时访问或更改数据库中的相同数据的情况。 并发控制:指的是用于在发生并发更改时确保数据一致性的特定机制。
并发标记的实现是通过EF Core来实现并发冲突的解决,而非在数据库层面的方案。EF Core 实现了乐观并发控制(非数据库层面),这意味着它将允许多个进程或用户独立进行更改,而不会产生同步或锁定的开销。 在理想情况下,这些更改将不会相互干扰,因此都能够成功。 在最坏的情况下,两个或更多进程将尝试进行冲突更改,而其中只有一个进程会成功。
4.1 并发控制在 EF Core 中的工作原理:
配置为并发标记的属性用于实现乐观并发控制:每当在
SaveChanges期间执行更新或删除操作时,会将数据库上的并发标记属性值与通过 EF Core 读取的原始值进行比较:
(1) 如果这些值匹配,则可以完成该操作。
(2) 如果这些值不匹配,EF Core 会假设另一个用户已执行冲突操作,并中止当前事务。这种情况被称为"并发冲突"。
当配置好并发标记后,数据库提供程序负责实现并发标记值的比较,EF Core 会对任何 UPDATE 或 DELETE 语句的 WHERE 子句中的并发标记值进行检查。执行这些语句后,EF Core 会读取受影响的行数。如果未影响任何行,将检测到并发冲突,并且 EF Core 会引发 DbUpdateConcurrencyException。
例如,将 Person 的 LastName 配置为并发标记。 这样,对 Person 的任何更新操作(并发标记的属性必须作为比较参照来反映并发冲突),都将在 WHERE 子句中做并发检查,在数据库端将执行如下sql命令,条件LastName 是EF自动加上去:
UPDATE [Person] SET [FirstName] = @p1 WHERE [PersonId] = @p0 AND [LastName] = @p2;
并发标记后会有三组值可用于帮助解决并发冲突:
1.“当前值”是应用程序尝试写入数据库的值。
2.“原始值”是在进行任何编辑之前最初从数据库中检索的值。
3.“数据库值”是当前存储在数据库中的值。
产生并发冲突的常规处理步骤是:
1.在 SaveChanges 期间捕获 DbUpdateConcurrencyException。
2.使用 DbUpdateConcurrencyException.Entries 为受影响的实体准备一组新更改。
3.刷新并发标记的原始值以反映数据库中的当前值。
4.重试该过程,直到不发生任何冲突。
下面使用数据注释方法将LastName属性配置为并发标记
public class Person { public int PersonId { get; set; } [ConcurrencyCheck] public string LastName { get; set; } public string FirstName { get; set; } }
下面使用Fluent API 可用于将LastName属性配置为并发标记
modelBuilder.Entity<Person>().Property(p => p.LastName).IsConcurrencyToken();
总结:并发冲突的产生,一般只有在生产环境或模拟大量用户线程的情况下才可能产生关系型数据库的并发死锁。产生死锁的原因有很多,如未建索引导致表扫描、或未按同一顺序访问对象等。只有分析出死锁的原因( sqlserver 死锁分析) (mysql死锁分析),才考虑结合EF的并发标记来解决。
参考文献:
官方资料: 主键
- asp.net core系列 23 EF模型配置(概述, 类型和属性的包含与排除)
- ASP.NET MVC 3 配置EF自动生成模型
- asp.net core中使用EF Core自动生成表的Id主键
- asp.net core 系列 20 EF基于数据模型创建数据库
- ASP.NET Core的配置(2):配置模型详解
- 学习ASP.NET Core Razor 编程系列八——并发处理
- ASP.Net Core 中使用Zookeeper搭建分布式环境中的配置中心系列一:使用Zookeeper.Net组件演示基本的操作
- IT咨询顾问:一次吐血的项目救火 java或判断优化小技巧 asp.net core Session的测试使用心得 【.NET架构】BIM软件架构02:Web管控平台后台架构 NetCore入门篇:(十一)NetCore项目读取配置文件appsettings.json 使用LINQ生成Where的SQL语句 js_jquery_创建cookie有效期问题_时区问题
- 2.3Options建立配置和实体的映射「深入浅出ASP.NET Core系列」
- 使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【四】——实现模型工厂,依赖注入以及格式配置
- asp.net core高级EF Core2.0数据模型设计创建
- 2.1命令行和JSON的配置「深入浅出ASP.NET Core系列」
- 一起学ASP.NET Core 2.0学习笔记(二)- ef core2.0 及mysql provider 、Fluent API相关配置及迁移
- asp.net core 系列 10 配置configuration (上)
- 从头编写 asp.net core 2.0 web api 基础框架 (4) EF配置
- ASP.NET Core的配置(2):配置模型详解
- 2.2Bind建立配置文件和实体的映射「深入浅出ASP.NET Core系列」
- 从头编写 asp.net core 2.0 web api 基础框架 (4) EF配置
- 2.3Options建立配置和实体的映射「深入浅出ASP.NET Core系列」
- C#编译器优化那点事 c# 如果一个对象的值为null,那么它调用扩展方法时为甚么不报错 webAPI 控制器(Controller)太多怎么办? .NET MVC项目设置包含Areas中的页面为默认启动页 (五)Net Core使用静态文件 学习ASP.NET Core Razor 编程系列八——并发处理