Entity Framework Code First实现乐观并发
Entity Framework Code First实现乐观并发
不定时更新翻译系列,此系列更新毫无时间规律,文笔菜翻译菜求各位看官老爷们轻喷,如觉得我翻译有问题请挪步原博客地址
本博文翻译自:
https://www.codeproject.com/Articles/817432/Optimistic-Concurrency-in-Entity-Framework-Code-Fi
介绍
本文描述了使用Entity Framework Code First处理乐观并发性的不同配置
并发性
在计算机科学中,并发性是系统的一个属性,在这个系统中,多个计算同时执行,并且有可能相互影响。
在web应用程序中,这是一个多用户环境,在保存数据库中的数据时,可能存在并发性。并发性大致分为两种类型:1)悲观并发2)乐观并发
1) 悲观并发
数据库中的悲观并发包括锁行,以防止其他用户以影响当前用户的方式修改数据。
在这种方法中,用户执行一个操作,其中一个锁被应用,其他用户不能在该记录上执行相同的操作,直到该锁被释放。
2) 乐观并发
相比之下,在乐观并发中,当用户阅读时,行不会被锁定。当用户试图更新这一行时,系统必须确定该记录是否被另一个用户修改过,因为它被读取了。
开始编写代码
让我们创建一个控制台应用程序来探索处理乐观并发的不同情况。
步骤
- 使用Visual Studio,创建控制台应用程序(文件 ->新建->项目->控制台程序(使用 Visual C# 模板)) 并将其命名为 ConcurrencyCheck.
- 向项目添加一个新的文件夹 Models. 然后在这个文件夹中添加两个类文件: EducationContext.cs 和 Student.cs .
- 在这个控制台应用程序中安装EntityFramework Nuget包。在包管理器控制台中运行"_Install-Package EntityFramework"_命令来执行此操作。或者,您也可以"Nuget包管理器"理窗口安装相同的程序。
下表显示了用于乐观并发的不同配置。
配置乐观并发
| Convention | None |
|---|---|
| Data Annotation | [Timestamp] |
| Fluent API | .IsRowVersion() |
1) Convention
Entity Framework Code First 没有任何处理乐观并发的约定。您可以使用Data Annotation或Fluent API来处理乐观并发。
2) Data Annotation
Code First使用**[Timestamp] **处理乐观并发性的属性。
a) 修改 EducationContext.cs 文件如下:
using System.Data.Entity;
namespace ConcurrencyCheck.Models
{
class EducationContext : DbContext
{
public EducationContext()
: base("EducationContext")
{
}
public DbSet<Student> Students { get; set; }
}
}
在base("EducationContext")中通过Code First 指令在 App.config 文件中使用名为"EducationContext"的连接字符串
b) 修改 Student.cs 文件如下:
using System.ComponentModel.DataAnnotations;
namespace ConcurrencyCheck.Models
{
public class Student
{
public int StudentId { get; set; }
public string RollNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
}
请注意,在 Student 类中有一个属性 RowVersion ,它是 byte[] 类型,并被分配使用 [Timestamp] 属性来处理乐观的并发性。
c) 将 App.config 文件中的连接字符串更改为指向一个有效的数据库:
<connectionStrings>
<add name="EducationContext" providerName="System.Data.SqlClient" connectionString="Server=DUKHABANDHU-PC; Database=ConcurrencyCheck;Integrated Security=SSPI" />
</connectionStrings>
在这里,我们将数据库名称作为ConcurrencyCheck,它将在应用程序运行时通过 Code First 创建。
d) 修改 Program.cs 文件在每次应用程序运行时都要删除和创建数据库:
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<EducationContext>());
using (var context = new EducationContext())
{
context.Students.Add(new Student
{
FirstName = "Dukhabandhu",
LastName = "Sahoo",
RollNumber = "1"
});
context.SaveChanges();
}
Console.WriteLine("Database Created!!!");
Console.ReadKey();
}
如果运行该应用程序,代码首先将创建数据库 ConcurrenCheck 它拥有两个表 MigrationHistory 和 Students 。
如果您看到 Students 表中的 RowVersion 列(在SQL Sever中),它的数据类型是 timestamp 。

RowVersion 和 TimeStamp 是不同数据库提供程序用于相同目的的两个术语。当创建或更新 Students 表中的记录时,数据库将自动更新 RowVersion 值到新值。即使您为 rowversion 列发送值,数据库(SQL Server)也不使用该值来进行插入或更新操作。
当添加到 Students 表的新记录时生成的SQL:
exec sp_executesql N'INSERT [dbo].[Students]([RollNumber], [FirstName], [LastName])
VALUES (@0, @1, @2)
SELECT [StudentId], [RowVersion]
FROM [dbo].[Students]
WHERE @@ROWCOUNT > 0 AND [StudentId] = scope_identity()',N'@0 nvarchar(max) ,@1 nvarchar(max) ,@2 nvarchar(max) ',@0=N'1',@1=N'Dukhabandhu',@2=N'Sahoo'
您可以看到查询不仅插入了一个新记录,而且还返回了 RowVersion 的值。
当进行更新和删除操作时,会发生实际的并发检查。在更新和删除 Students 表的记录时,请参阅下面如何发生并发检查。
UPDATE SQL
exec sp_executesql N'UPDATE [dbo].[Students]
SET [RollNumber] = @0 WHERE (([StudentId] = @1) AND ([RowVersion] = @2))
SELECT [RowVersion] FROM [dbo].[Students]
WHERE @@ROWCOUNT > 0 AND [StudentId] = @1',N'@0 nvarchar(max) ,@1 int,@2 binary(8)',@0=N'2',@1=1,@2=0x00000000000007D1
我们看到 WHERE 条件,在更新记录时,它比较了 studentid (主键)和 RowVersion 。
DELETE SQL
exec sp_executesql N'DELETE [dbo].[Students]
WHERE (([StudentId] = @0) AND ([RowVersion] = @1))',N'@0 int,@1 binary(8)',@0=1,@1=0x00000000000007D1
在删除记录代码之前,先创建一个查询来比较标识符(主键 StudentId )和行版本( RowVersion 字段)用于乐观并发。
3) Fluent API
Fluent API使用 IsRowVersion() 方法来配置乐观并发。
为了测试Fluent API的配置,从 Students 类的 RowVersion 属性中删除 [Timestamp] 属性,并在 EducationContext 类中覆盖 ** onmodel()** 方法:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().Property(s => s.RowVersion).IsRowVersion();
base.OnModelCreating(modelBuilder);
}
配置非时间戳字段
如果不保留对并发检查的专用列,您仍然可以处理并发性。有些数据库不支持 RowVersion / Timestamp 类型的列。在这些类型的场景中,您可以使用Data Annotation或Fluent API配置来配置一个或多个用于并发检查的字段。
配置非时间戳字段
| Convention | None |
|---|---|
| Data Annotation | [ConcurrencyCheck] |
| Fluent API | .IsConcurrencyToken() |
1) Data Annotation
修改 Student 类,以使用 [ConcurrencyCheck] Data Annotation属性:
public class Student
{
public int StudentId { get; set; }
[ConcurrencyCheck]
public string RollNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
当应用程序运行时,代码首先创建 Students 表(参见下图)。数据库在 RollNumber 列中不为 [ConcurrencyCheck] 属性做任何特殊的事情。

但是当任何修改/更改发生在Students表时,代码首先处理并发检查。接下来阅读代码如何创建更新和删除并处理并发检查。
UPDATE SQL
exec sp_executesql N'UPDATE [dbo].[Students]
SET [RollNumber] = @0
WHERE (([StudentId] = @1) AND ([RollNumber] = @2))
',N'@0 nvarchar(max) ,@1 int,@2 nvarchar(max) ',@0=N'2',@1=1,@2=N'1'
注意 WHERE 条件。它在更新记录时比较 StudentId (主键)和 RollNumber 。
DELETE SQL
exec sp_executesql N'DELETE [dbo].[Students]
WHERE (([StudentId] = @0) AND ([RollNumber] = @1))',N'@0 int,@1 nvarchar(max) ',@0=1,@1=N'2'
在删除 Students 表的记录时,它还检查 StudentId 和 RollNumber 列值。如果 RollNumber 列值改变了,你现在正在更新那个记录,然后你就会得到OptimisticConcurrencyException。
2) Fluent API
使用Code First的 IsConcurrencyToken() 方法来处理非时间戳字段的并发性。
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().Property(s => s.RollNumber).IsConcurrencyToken();
base.OnModelCreating(modelBuilder);
}
注意
为了测试并发效果,添加代码来更新Students表中已有的记录如下:
var student = context.Students.FirstOrDefault(u => u.StudentId == 1);
if (student != null)
{
student.RollNumber = "2";
context.SaveChanges();
}
在Visual Studio中为 context.SaveChanges() 行添加断点。在 SaveChanges() 方法执行之前,修改数据库中 Students 表记录条件是StudentId = 1。
UPDATE Students SET RollNumber = '123' WHERE StudentId = 1;
现在,如果你要执行下一行语句savechanges()然后你会得到如下的一个提示:
DbUpdateConcurrencyException:
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.
DbUpdateConcurrencyException 异常抛出,因为记录已经修改为只读。
总结
在本文中,我们学习了如何通过Entity Framework Code First将专用字段保存在表中,或者通过添加特殊data annotation属性或Fluent API配置来配置处理乐观并发性的方法。
欢迎转载,转载请注明翻译原文出处(本文章),原文出处(原博客地址),然后谢谢观看
如果觉得我的翻译对您有帮助,请点击推荐支持:)
Entity Framework Code First实现乐观并发的更多相关文章
- Entity Framework Code First (三)Data Annotations
Entity Framework Code First 利用一种被称为约定(Conventions)优于配置(Configuration)的编程模式允许你使用自己的 domain classes 来表 ...
- Entity Framework Code First学习系列目录
Entity Framework Code First学习系列说明:开发环境为Visual Studio 2010 + Entity Framework 5.0+MS SQL Server 2012, ...
- Entity Framework Code First数据库连接
1. 安装Entity Framework 使用NuGet安装Entity Framework程序包:工具->库程序包管理器->程序包管理器控制台,执行以下语句: PM> Insta ...
- Entity Framework Code First属性映射约定
Entity Framework Code First与数据表之间的映射方式有两种实现:Data Annotation和Fluent API.本文中采用创建Product类为例来说明tity Fram ...
- Entity Framework Code First关系映射约定
本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个 ...
- Entity Framework Code First执行SQL语句、视图及存储过程
1.Entity Framework Code First查询视图 Entity Framework Code First目前还没有特别针对View操作的方法,但对于可更新的视图,可以采用与Table ...
- Entity Framework Code First使用DbContext查询
DbContext.DbSet及DbQuery是Entity Framework Code First引入的3个新的类,其中DbContext用于保持数据库会话连接,实体变化跟踪及保存,DbSet用于 ...
- Entity Framework Code First添加修改及删除单独实体
对于一个单独实体的通常操作有3种:添加新的实体.修改实体以及删除实体. 1.添加新的实体 Entity Framework Code First添加新的实体通过调用DbSet.Add()方法来实现. ...
- Entity Framework Code First实体对象变动跟踪
Entity Framework Code First通过DbContext.ChangeTracker对实体对象的变动进行跟踪,实现跟踪的方式有两种:变动跟踪快照和变动跟踪代理. 变动跟踪快照:前面 ...
随机推荐
- dubbo+zookeeper伪集群配置
zookeeper作为注册中心,服务器和客户端都要访问,如果有大量的并发,肯定会有等待.所以可以通过zookeeper集群解决. 一.为什么需要zookeeper呢? 大部分分布式应用需要一个主控.协 ...
- Web in Linux小笔记001
Linux灾难恢复: Root密码修复 Centos single Filesystem是硬盘文件根目录,无法再cd ..就像macitosh 硬盘图标 Pwd:显示绝对路径 MBR修复 模拟MBR被 ...
- Mac Intellij Debug 模式
前言:本文的核心是记录debug的使用,与具体的操作系统无关. 1.什么是Debug Debug是程序的一种运行模式.用来发掘程序的走向,以及程序在运行过程中参数的变化. 2.Debug的作用 deb ...
- 【集美大学1411_助教博客】团队作业9——测试与发布(Beta版本)
写在前面的话 已经看到了大家的发布成果,很欣喜,虽然有的团队的产品还是有一点问题,但大家也都发布成功了,这就是软件的魅力.但还是要说一些问题,大家录的视频不是没人讲解就是讲得太快,在我看来这都没有在卖 ...
- 【Beta阶段】计划安排
一.新成员介绍 姓名 陈雄 学号 106 角色 前端 个人相片 二.完善功能 登录注册 记分板 排行榜 界面优化 三.新增功能 换肤(可以一试) 联网PK 分享邀请 四.团队 ...
- Win8打开chm右侧空白解决方法
Win8下打开CHM文件,左侧有目录,但是右侧空白.而且打开的时候,还弹出很多IE窗口. 感觉应该不是文件本身的问题.下面是我的解决方法,其他系统也可以试一试. 最初打开文件如下 首先:1,右键关联c ...
- 201521123009《Java程序设计》第14周学习总结
1. 本周学习总结 2. 书面作业 1. MySQL数据库基本操作 建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现自己的学号.姓名) 在自己建立的数据库上执行常见SQL语句(截图) - ...
- 201521123066《Java程序设计》第十三周学习总结
1. 本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 2. 书面作业 1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jmu ...
- PIC单片机状态寄存器中的C(进位/借位位标志)
查阅PIC单片机芯片手册,关于进位/借位位的说明为: C:进位/借位位. 1 = 结果的最高位发生了进位 0 = 结果的最高位未发生进位 同时有一条标注:借位的极性是相反的. 通过以上说明,可以将C的 ...
- Vagrant下共享目录下静态文件(js/jpg/png等)修改完运行报错
利用Vagrant部署开发环境,使用目录共享模式,在本地磁盘进行开发,通过虚拟机环境运行开发的页面. 接下来打开页面,看上去一切正常,接下来将发生一个神奇的事情,你修改一个css文件,在刷新浏览器,发 ...