前面的6篇博客基本把Code First学习的差不多了,今天这篇学习下code first中的并发控制和事务,基本也快学完了,顶多就差数据迁移。

在数据库中也是有锁和事务的概念,在C#中也是存在,当然code first也是必要要有的。对于什么是并发、什么是锁和事务,它们的特性是什么这些概念性的就不一一列举。因为这些要是发散的学习就涉及到好多的知识点。并发能联想到多线程,多线程能联想到同步异步,同步异步操作系统,等等。知识都是连贯着的。有了并发那就有不并发的情况,想让不并发,那用什么呢?那就要用锁。说起锁又想起锁的类型、粒度、死锁等。什么悲观锁、乐观锁、粒度上又分单元格级、行级、表级等。总的来说想弄明白C#是很不容易的,担负着打光棍的风险,把青春放在了敲代码上。上面算是开场白一些不正经的,下面开始说正经的。

一、并发控制

1.锁分悲观锁和乐观锁。

在code first中使用的是乐观锁。至于什么是悲观锁,大家应该都用过版本控制软件,VSS中有签入签出,当被迁出的时候其他人是修改不了的,这种就是可以理解为悲观锁。而在svn中当一个文件被迁出时,其他人还可以修改,之前commit的时候根据版本合并就OK,这种可以理解为乐观锁。

2.锁的粒度

code first在锁的粒度上也分为两种。一是行级一个单元格级及属性集。

1).行级

使用Timestamp来控制每行的并发。一张表中只能有一个Timestamp的字段(一个类只能有一个Timestamp的属性).

Data Annotations中用Timestamp来标识设置并发控制字段,标识为Timestamp的字段必需为byte[]类型。

    public class Person
{
public int PersonId { get; set; }
public int SocialSecurityNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public decimal Money { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
            var person = new Person
{
FirstName = "Cui",
LastName = "YanWei",
SocialSecurityNumber =
};
//新增一条记录,保存到数据库中
using (var con = new EFCodeFirstDbContext())
{
con.Persons.Add(person);
con.SaveChanges();
} var firContext = new EFCodeFirstDbContext();
//取第一条记录,并修改一个字段:这里是修改了FirstName
//先不保存
var p1 = firContext.Persons.FirstOrDefault();
p1.FirstName = "CuiA"; //再创建一个Context,同样取第一条记录,修改LastName字段并保存
using (var secContext = new EFCodeFirstDbContext())
{
var p2 = secContext.Persons.FirstOrDefault();
p2.LastName = "Ivan";
secContext.SaveChanges();
}
try
{
firContext.SaveChanges();
Console.WriteLine(" 保存成功");
}
catch (DbUpdateConcurrencyException ex)
{
Console.WriteLine(ex.Entries.First().Entity.GetType().Name + " 保存失败");
}
Console.Read();

在上面的代码中首先是新增一个Person对象,SaveChanges()保存到数据库。然后将Person对象获取出来改变FirstName,但此时并没有SaveChanges()保存到数据库.然后呢有将该对象取出来,修改LastName并SaveChanges()保存到数据库,此时再将上面的修改FirstName的保存到数据库,运行时会抛出异常。这是为什么呢?

可以看下下面的三个截图:

上面的3个截图,第一个sql是最先执行,然后是第二个,不过这里要注意下两个sql的binary字段,两个都是一样的,但是最后存在数据库的可是不一样,其实当第一个sql执行完时binary字段变成了也就是数据库现在存在的值,但是第二个sql执行时还是用的之前的值,所以就更新失败抛出异常。这就是Timestamp的机制。

2).列级

Data Annotations中用ConcurrencyCheck来标识控制列的并发。此时可以先把上面的Person类中的Timestamp注释掉。在SocialSecurityNumber属性上添加约定。

    public class Person
{
public int PersonId { get; set; } [ConcurrencyCheck]
public int SocialSecurityNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public decimal Money { get; set; }
//[Timestamp]
public byte[] RowVersion { get; set; }
}

我们将SocialSecurityNumber(社会保险号)标识为开放式并发。

            var person = new Person
{
FirstName = "Cui",
LastName = "YanWei",
SocialSecurityNumber =
};
//新增一条记录,保存到数据库中
using (var con = new EFCodeFirstDbContext())
{
con.Persons.Add(person);
con.SaveChanges();
}
var firContext = new EFCodeFirstDbContext();
//取第一条记录,并修改一个字段:这里是修改了SocialSecurityNumber=123
//先不保存
var p1 = firContext.Persons.FirstOrDefault();
p1.SocialSecurityNumber = ;
//再创建一个Context,同样取第一条记录,修改SocialSecurityNumber=456并保存
using (var secContext = new EFCodeFirstDbContext())
{
var p2 = secContext.Persons.FirstOrDefault(); p2.SocialSecurityNumber = ;
secContext.SaveChanges(); }
try
{
firContext.SaveChanges();
Console.WriteLine(" 保存成功");
}
catch (DbUpdateConcurrencyException ex)
{
Console.WriteLine(ex.Entries.First().Entity.GetType().Name + " 保存失败");
}
Console.Read();

下面还是3张截图.

和Timestamp类似,3个截图的第一个sql是第二个更改操作的数据库上下文更改执行的sql,第二个是第一个更改操作的数据库上下文更改执行的sql,where语句中增加了SocialSecurityNumber,由于第一个sql执行之后SocialSecurityNumber已经改变,第二个sql还是使用旧的SocialSecurityNumber,所以更新失败。

二、事务

1.默认事务处理

EF的默认行为是,无论何时执行任何涉及Create,Update或Delete的查询,都会默认创建事务。当DbContext类上的SaveChanges()方法被调用时,事务就会提交。

2.分布式事务

对于分布式事务可以想到TransactionScope,code first中分布式事务意味着多个DbContext,由于EF默认就是事务处理,所以只需关注分布式的事务,对于分布式事务的应用需要开启window服务,这个日后会具体总结。注意要引入类库:

using System.Transactions;
            using (var ts = new TransactionScope(TransactionScopeOption.Required))
{
//db数据库上下文操作
ts.Complete();
}

3.EF6管理事务

从EF 6起,EF在DbContext对象上提供了Database.BeginTransaction()方法,当使用上下文类在事务中执行原生SQL命令时,这个方法特别有用。

            using (var db = new EFCodeFirstDbContext())
{
using (var trans = db.Database.BeginTransaction())
{
try
{ //执行sql
db.SaveChanges(); trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
}
}
}

最新文章

  1. Window 消息大全
  2. GitHub初体验(小菜新手github用起来)
  3. ICE——1.Printer
  4. 通过DOS、SHELL批处理命令加载Lib并编译和打包Java项目(或者运行项目)
  5. HDOJ/HDU 2087 剪花布条(indexOf()应用~~)
  6. 杭电 HDU 1242 Rescue
  7. IDEA 护眼色设置 背景行颜色取消等设置
  8. 转载微信公众号 测试那点事:Jmeter乱码解决
  9. 关于px,分辨率,ppi的辨析
  10. 安装版本( 相关关系 Vue SSR 指定版本)
  11. [转]webpack中require和import的区别
  12. idea spring boot
  13. win10下Redis安装
  14. Android24以上拍照代码
  15. csp20151203画图 解题报告和易错地方
  16. linux strtock()函数使用问题
  17. JSP输出当前日期
  18. 2018-2019-1 20189218《Linux内核原理与分析》第五周作业
  19. HDU 6096 String (AC自动机)
  20. “System.InvalidOperationException”类型的未经处理的异常在 ESRI.ArcGIS.AxControls.dll 中发生

热门文章

  1. maven中把依赖的JAR包一起打包(转)
  2. poj 3159 Candies 差分约束
  3. 记一次Web应用CPU偏高
  4. c++ Windows Socket实现最简单的C/S网络通信(TCP)
  5. 2014 Super Training #8 G Grouping --Tarjan求强连通分量
  6. Android中Spinner下拉列表(使用ArrayAdapter和自定义Adapter实现) .
  7. zepto.js 源码解析
  8. 浅析CSS——元素重叠及position定位的z-index顺序
  9. 系统广播 android.intent.action.KILL_BACKGROUND_SERVICE
  10. U3D assetbundle加载与卸载的深入理解