EntiryFramework中事务操作实例

一、在EF中使用事务操作

1.EF中的事务操作默认情况下会阻塞其他进程的读取操作

2.分布式事务TranscationCope需引用System.data.Transcations.dll才能使用,TranscationCope进行多事务协调时需安装与设置MSDTC组件。

3.分布式事务与EF事务二者区别,主要区别在于前者用于一个数据连接中控制数据一致性,后者在多个数据库连接中控制数据一致性,如果不能理解,可以记忆为一个数据库使用事务用前者,多个数据库中使用事务用后者,如果在一个数据连接中使用TranscationCope,可以把它为理解为简化版的事务,因为它只有Complete方法。

二、在EF中使用数据库的事务DbContextTransaction

这种方式使用方便,但是在指定高级别锁定时,多线程有的会抛出异常,具体还不明。

该类可以继承使用。

    //
    // 摘要:
    //     包装对基础存储连接的事务对象的访问并确保实体框架在该事务的上下文内对数据库执行命令。通过对 System.Data.Entity.DbContextSystem.Data.Entity.Database
    //     对象调用 BeginTransaction() 来检索此类的实例。
    public class DbContextTransaction : IDisposable

如果不需要特殊的事务级别控制推荐使用这种方式。

Test1 _context = new Test1();
//1.使用数据库的事务时,多线程中IsolationLevel.RepeatableRead 出现回滚异常: 基础提供程序在 Rollback 上失败。
// System.Data.Entity.DbContextTransaction tran = _context.Database.BeginTransaction(System.Data.IsolationLevel.RepeatableRead);
System.Data.Entity.DbContextTransaction tran = _context.Database.BeginTransaction(System.Data.IsolationLevel.ReadCommitted);
using (tran)
{
    try
    {
        //1.修改省
        Area province = _context.Areas.FirstOrDefault(q => q.AreaLevel == 1);
        province.AreaName = province.AreaName + "1";
        _context.SaveChanges();
        Console.WriteLine(_context.Areas.FirstOrDefault(q => q.AreaLevel == 1).AreaName);
        //2.修改市
        Area city = _context.Areas.FirstOrDefault(q => q.AreaLevel == 2);
        city.AreaName = city.AreaName + "1";
        _context.SaveChanges();
        //抛出异常
        throw new Exception("测试事务异常");
        tran.Commit();
    }
    catch (Exception ex)
    {
        Console.WriteLine("执行出错:" + ex.Message);
        tran.Rollback();
    }
}

特别说明:

1.同一个事务处理中,不能出现两个数据库上下文实例,否则会出现死锁

2.还有个麻烦点,就是如果抛出异常执行回滚,EF实体缓存中的数据就和数据库出现不对应的现象,如果你的程序中上下文只有一个。解决方案参考:EntiryFramework中事务操作(三)事务回滚数据模型和数据库不对应问题

会出现死锁的代码实例:

Test1 _context = new Test1();
Test1 _context2 = new Test1();

System.Data.Entity.DbContextTransaction tran = _context.Database.BeginTransaction();
using (tran)
{
    try
    {
        //1.修改省
        Area province = _context.Areas.FirstOrDefault(q => q.AreaLevel == 1);
        province.AreaName = province.AreaName + "1";
        _context.SaveChanges();
        Console.WriteLine(_context2.Areas.FirstOrDefault(q => q.AreaLevel == 1).AreaName);
        //2.修改市
        Area city = _context2.Areas.FirstOrDefault(q => q.AreaLevel == 2);
        city.AreaName = city.AreaName + "1";
        _context2.SaveChanges();
        //抛出异常
        throw new Exception("测试事务异常");
        tran.Commit();
    }
    catch (Exception ex)
    {
        Console.WriteLine("执行出错:" + ex.Message);
        tran.Rollback();
    }
}

三、在EF中使用打开连接的事务 DbTransaction

Test1 _context = new Test1();
//事务锁定行为
//1.使用数据库链接的事务时,RepeatableRead:在多线程时,会保证一次执行成功,其他在当前事务期间启动的事务异常结束
//2.使用数据库链接的事务时,ReadCommitted:不会阻塞当前进程内的其他线程的事务提交
System.Data.Common.DbConnection con = _context.Database.Connection;
if (con.State != System.Data.ConnectionState.Open)
    con.Open();
System.Data.Common.DbTransaction tran = con.BeginTransaction();
_context.Database.UseTransaction(tran);
using (tran)
{
    try
    {
        Thread.Sleep(number * 1000);
        //1.修改省
        Area province = _context.Areas.FirstOrDefault(q => q.AreaLevel == 1);
        province.AreaName = province.AreaName + "1";
        _context.SaveChanges();
        Thread.Sleep(number * 500);
        Console.WriteLine(_context.Areas.FirstOrDefault(q => q.AreaLevel == 1).AreaName);
        //2.修改市
        Area city = _context.Areas.FirstOrDefault(q => q.AreaLevel == 2);
        city.AreaName = city.AreaName + "1";
        _context.SaveChanges();
        //抛出异常
        throw new Exception("测试事务异常");
        tran.Commit();
    }
    catch (Exception ex)
    {
        Console.WriteLine("执行出错:" + ex.Message);
        tran.Rollback();
    }
}

特别说明:

DbContextTransaction的多个上下文问题和异常问题,在 DbTransaction也会出现


四、更多事务级别说明

1)    使用事务级别ReadUnCommited 会产生脏读现像,意味着读取到的为UnCommited(未提交)的数据。怎么理解呢?在使用该隔离级别的事务开始后。更新了数据库某一行的数据,但是事务的工作量比较大,后续还有一大堆代码还没执行完呢。不巧的是有个哥们过来读数据了,这个时候读到的就是未提交的值,如果后继工作一切正常,也没什么影响。一旦后面的代码执行中出错,就会产生不一致的错误,适用于对事务极度自信的情况下,特点为可读不可改。关于不可改需解释一下,MS SQL中一条语句为最基本的执行单元了,如果一个事务中,对同条数据的更新语句未Commited的情况下,其它事务是需要等待的。

2) 使用事务级别ReadCommited 会消除脏读现像,意味着读取到的为Commited(已提交)的数据。在使用该隔离级别的事务开始后,除了该事务其它的查询均会一直等待直到该事务提交。特点为不可读不可改,

3) 使用事务级别Repeatable Read,可重复读。它不会像ReadCommited一样阻止Select查询,但它会阻止Update语句。使用该隔离级别的事务开始后,会阻止其它事务更改查询。但依然可以新增数据。

4) 使用事务级别Serializable 最严的一种了,与Repeatable Read相比就是在这种级别下不能新增数据。

5)  下表为简单说明

其中增删除改查为其它事务中对已在事务中的数据行进行增删改查的可能行性其中删,改,查操作多个事务中的同一条数据。√指该隔离级别下操作可未被阻塞可立即响应

隔离级别

增(其他事务)

删(其他事务)

改(其他事务)

查(其他事务)

脏读

读提交

可重复读

幻影读

数据过期

ReadUnCommited

×

×

×

×

ReadCommited

×

×

×

×

×

×

Repeatable Read

×

×

×

Serializable

×

×

×

×

×

更多:

EntiryFramework中事务操作(二)

EntiryFramework中事务操作(三)事务回滚数据模型和数据库不对应问题

EntityFramework中JSON序列化循环引用----JavaScriptSerializer

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值