一. 什么是事务

  我们通俗的理解事务就是一系列操作要么全部成功、要么全部失败(不可能存在部分成功,部分失败的情况)。

  举一个事务在我们日常生活中的经典例子:两张银行卡(甲、乙),甲向乙转钱,整个过程需要执行两个操作,甲账户钱减少,乙账户钱增加,这是转账成功的情况;转账失败时候,二者均不执行,甲乙账户钱都不变。

1. SQLServer中的事务

  SQLServer中的事务具体封装方式有多种,但无论是哪种,都需要依赖于这三句代码:begin transaction 、commit transaction、rollback transaction。 

   具体怎么使用,详见数据库事务章节:去复习

2. EF中的事务

  EF中的事务主要分为三类,分别是SaveChanges、DbContextTransaction、TransactionScope。

   需要掌握的是这三种事务各自的使用方式和适用场景。

二. SaveChanges

  SaveChanges是EF中最常见的事务了,在前面章节,多次提到,SaveChanges一次性将本地缓存中所有的状态变化一次性提交到数据库,这就是一个事务,要么统一成功,要么统一回滚。

   应用场景:该种事务模式在实际开发中非常常用,在处理模块业务同时需要对一张表(或多张表)进行一系列增删改操作,但这些操作都是要么都成功,要么都失败,这种情况下可以在流程的最后 调用SaveChanges,一次性保存本地属性状态的全部变化。

  该模式作用于一个DBContext,即一个数据库的EF的上下文,不能控制多个数据库。

   比如:注册场景,可能需要向用户表、日志表、账户表等多张表中插入数据,其中一张表插入失败,所有的都需要回滚,这种情况,就可以在该业务的最后,统一调用SaveChanges一次性的事务提交。

1. 事务成功

      private static void TestSaveChanges()
{
using (DbContext db = new CodeFirstModel())
{
//增加
TestInfor t1 = new TestInfor()
{
id = Guid.NewGuid().ToString("N"),
txt1 = "txt1",
txt2 = "txt2"
};
db.Set<TestInfor>().Add(t1);
//删除
TestInfor t2 = db.Set<TestInfor>().Where(u => u.id == "").FirstOrDefault();
if (t2 != null)
{
db.Set<TestInfor>().Remove(t2);
}
//修改
TestInfor t3 = db.Set<TestInfor>().Where(u => u.id == "").FirstOrDefault();
t3.txt2 = "我是李马茹23"; //SaveChanges事务提交
int n = db.SaveChanges();
Console.WriteLine("数据作用条数:" + n);
}
}

2. 事务回滚

       private static void TestSaveChangesFailure()
{
using (DbContext db = new CodeFirstModel())
{
//增加
TestInfor t1 = new TestInfor()
{
id = Guid.NewGuid().ToString("N"),
txt1 = "txt1",
txt2 = "txt2"
};
db.Set<TestInfor>().Add(t1);
//删除
TestInfor t2 = db.Set<TestInfor>().Where(u => u.id == "").FirstOrDefault();
if (t2 != null)
{
db.Set<TestInfor>().Remove(t2);
}
//修改
TestInfor t3 = db.Set<TestInfor>().Where(u => u.id == "").FirstOrDefault();
t3.txt2 = "我是李马茹23";
//增加(制造错误)
TestInfor t4 = new TestInfor()
{
id = Guid.NewGuid().ToString("N") + "",
txt1 = "txt1",
txt2 = "txt2"
};
db.Set<TestInfor>().Add(t4); //SaveChanges事务提交
int n = db.SaveChanges();
Console.WriteLine("数据作用条数:" + n);
}
}

三. DbContextTransaction事务

  该事务为EF6新增的事务,通常用于手动接管事务,某些操作是一个事务,某些操作是另外一个事务。

  使用场景:EF调用SQL语句的时候使用该事务、 多个SaveChanges的情况(解决自增ID另一个业务中使用的场景)。

  核心代码:BeginTransaction、Commit、Rollback、Dispose. 如果放到using块中,就不需要手动Dispose了。

  该种事务与数据库中的transaction事务原理一致,在EF中,由于每次调用 db.Database.ExecuteSqlCommand(sql1, pars1)的时候,即刻执行了该SQL语句,所以要把他放到一个大的事务中,整体提交、回滚.(EF6新增的)

1. 事务成功

      private static void TestDbContextTransaction()
{
using (DbContext db = new CodeFirstModel())
{
DbContextTransaction trans = null;
try
{
//开启事务
trans = db.Database.BeginTransaction();
//增加
string sql1 = @"insert into TestInfor values(@id,@txt1,@txt2)";
SqlParameter[] pars1 ={
new SqlParameter("@id",Guid.NewGuid().ToString("N")),
new SqlParameter("@txt1","txt11"),
new SqlParameter("@txt2","txt22")
};
db.Database.ExecuteSqlCommand(sql1, pars1);
//删除
string sql2 = @"delete from TestInfor where id=@id";
SqlParameter[] pars2 ={
new SqlParameter("@id","")
};
db.Database.ExecuteSqlCommand(sql2, pars2);
//修改
string sql3 = @"update TestInfor set txt1=@txt1 where id=@id";
SqlParameter[] pars3 ={
new SqlParameter("@id",""),
new SqlParameter("@txt1","二狗子")
};
db.Database.ExecuteSqlCommand(sql3, pars3); //提交事务
trans.Commit();
Console.WriteLine("事务成功了");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
trans.Rollback(); }
finally
{
trans.Dispose(); //也可以把该事务写到using块中,让其自己托管,就不需要手动释放了
}
}
}

2. 事务回滚

      private static void TestDbContextTransactionFailure()
{
using (DbContext db = new CodeFirstModel())
{
DbContextTransaction trans = null;
try
{
//开启事务
trans = db.Database.BeginTransaction();
//增加
string sql1 = @"insert into TestInfor values(@id,@txt1,@txt2)";
SqlParameter[] pars1 ={
new SqlParameter("@id",Guid.NewGuid().ToString("N")),
new SqlParameter("@txt1","txt11"),
new SqlParameter("@txt2","txt22")
};
db.Database.ExecuteSqlCommand(sql1, pars1);
//删除
string sql2 = @"delete from TestInfor where id=@id";
SqlParameter[] pars2 ={
new SqlParameter("@id","")
};
db.Database.ExecuteSqlCommand(sql2, pars2);
//修改
string sql3 = @"update TestInfor set txt1=@txt1 where id=@id";
SqlParameter[] pars3 ={
new SqlParameter("@id",""),
new SqlParameter("@txt1","二狗子222")
};
db.Database.ExecuteSqlCommand(sql3, pars3); //增加(制造错误)
string sql4 = @"insert into TestInfor values(@id,@txt1,@txt2)";
SqlParameter[] pars4 ={
new SqlParameter("@id",Guid.NewGuid().ToString("N")+""),
new SqlParameter("@txt1","txt11"),
new SqlParameter("@txt2","txt22")
};
db.Database.ExecuteSqlCommand(sql4, pars4); //提交事务
trans.Commit();
Console.WriteLine("事务成功了");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
trans.Rollback(); }
finally
{
trans.Dispose();
}
}
}

3. 自DbContextTransaction事务测试多个SaveChanges的情况统一回滚

   private static void DbContextTransSaveChanges()
{
using (DbContext db = new CodeFirstModel())
{
//自动脱管,不需要手动释放
using (DbContextTransaction trans = db.Database.BeginTransaction())
{
try
{
TestInfor t1 = new TestInfor()
{
id = Guid.NewGuid().ToString("N"),
txt1 = "",
txt2 = ""
};
db.Entry(t1).State = EntityState.Added;
db.SaveChanges(); //人为制造失败情况
TestInfor t2 = new TestInfor()
{
id = Guid.NewGuid().ToString("N") + "",
txt1 = "",
txt2 = ""
};
db.Entry(t2).State = EntityState.Added;
db.SaveChanges(); trans.Commit();
}
catch (Exception)
{
trans.Rollback();
} }
}
}

四. TransactionScope事务

1. 该事务用来处理多个SaveChanges的事务(特殊情况的业务)或者多个DBContext(每个DBContext是一个实例,代表不同的数据库连接).

2. 核心代码:(一个Complete函数走天下,异常的话,自动回滚 ,也可以结合try-catch Transaction.Current.Rollback();实现回滚)

         需要引入程序集:using System.Transactions;

3. 适用场景:

  ①该种事务适用于多数据库连接的情况

  特别注意:如果使用该事务来处理多个数据库(多个DBContex)时,必须手动开启msdtc服务,这样才可以将多个DB的SaveChange给放到一个事务中,如果失败, 则多个数据库的数据统一回滚.

    开启msdtc服务的步骤: cmd命令→net start msdtc

   ②主键id自增的情况,同一个业务线中,需要拿到新增加的数据的主键id,进行操作。

③多线程带锁的情况,同一条业务线前半部分必须先SaveChanges,才能保证数据准确性(测试简单版本,实际的业务场景待补充!!!)

4. 主键自增被使用的情况的解决方案

     private static void Test3()
{
using (DbContext db = new CodeFirstModel())
{
using (TransactionScope trans = new TransactionScope())
{
try
{
TestInfor2 t1 = new TestInfor2()
{
txt11 = Guid.NewGuid().ToString("N"),
txt22 = Guid.NewGuid().ToString("N")
};
db.Set<TestInfor2>().Add(t1);
//如果这里不写SaveChanges的话,t1.id永远为0
db.SaveChanges(); TestInfor2 t2 = new TestInfor2()
{
txt11 = (t1.id + ).ToString(),
txt22 = Guid.NewGuid().ToString("N")
};
db.Entry(t2).State = EntityState.Added;
db.SaveChanges(); //事务统一提交(若失败则统一回滚)
trans.Complete();
Console.WriteLine("自增提交成功");
}
catch (Exception)
{
Transaction.Current.Rollback();
}
}
}
}

5. 多个DBContext、多个数据库的情况。

  private static void TransactionScopeTwoDB()
{
using (TransactionScope trans = new TransactionScope())
{
try
{
DbContext db1 = new CodeFirstModel();
DbContext db2 = new CodeFirstModel2();
//数据库1
TestInfor t1 = new TestInfor()
{
id = Guid.NewGuid().ToString("N"),
txt1 = "",
txt2 = ""
};
db1.Entry(t1).State = EntityState.Added;
db1.SaveChanges(); //数据库2
TestOne t2 = new TestOne()
{
id = Guid.NewGuid().ToString("N"), //可以在此处手动制造个错误,来测试多数据库的回滚问题
t1 = "",
t2 = ""
};
db2.Entry(t2).State = EntityState.Added;
db2.SaveChanges(); trans.Complete();
Console.WriteLine("多个数据库添加成功");
}
catch (Exception)
{
Transaction.Current.Rollback();
} }
}

第七节: EF的三种事务的应用场景和各自注意的问题(SaveChanges、DBContextTransaction、TransactionScope)的更多相关文章

  1. ASP.NET MVC深入浅出(被替换) 第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式 第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery ) 第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法 第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性) 第十节: EF的三种追踪

    ASP.NET MVC深入浅出(被替换)   一. 谈情怀-ASP.NET体系 从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态 ...

  2. 第十节: EF的三种追踪实体状态变化方式(DBEntityEntry、ChangeTracker、Local)

    一. 简介 我们在前面章节介绍EF基本增删改的时候,曾说过EF的SaveChanges()方法,会一次性的将所有的实体的状态变化统一提交到数据库,那么你是否想过EF的实体会有哪些状态变化呢?什么原因会 ...

  3. 第十四节: EF的三种模式(四) 之 原生正宗的 CodeFirst模式的默认约定

    一. 简介 1. 正宗的CodeFirst模式是不含有edmx模型,需要手动创建实体.创建EF上下文,然后生成通过代码来自动映射生成数据库. 2. 旨在:忘记SQL.忘记数据库. 3. 三类配置:On ...

  4. 第十一节: EF的三种模式(一) 之 DBFirst模式(SQLServer和MySQL两套方案)

    一. 简介 EF连接数据库有三种模式,分别是DBFirst.ModelFirst.CodeFirst,分别适用于不同的开发场景. 该章节,将主要介绍EF的DBFirst连接SQLServer数据库和M ...

  5. Java面试题之Oracle 支持哪三种事务隔离级别

    Oracle 支持三种事务隔离级别: 1.读已提交:(默认) 2.串行化: 3.只读模式

  6. Spring事务Transactional和动态代理(三)-事务失效的场景

    系列文章索引: Spring事务Transactional和动态代理(一)-JDK代理实现 Spring事务Transactional和动态代理(二)-cglib动态代理 Spring事务Transa ...

  7. Collection集合重难点梳理,增强for注意事项和三种遍历的应用场景,栈和队列特点,数组和链表特点,ArrayList源码解析, LinkedList-源码解析

    重难点梳理 使用到的新单词: 1.collection[kəˈlekʃn] 聚集 2.empty[ˈempti] 空的 3.clear[klɪə(r)] 清除 4.iterator 迭代器 学习目标: ...

  8. Entity Framework 5.0系列之EF概览-三种编程方式

    概述 在开发面向数据的软件时我们常常为了解决业务问题实体.关系和逻辑构建模型而费尽心机,ORM的产生为我们提供了一种优雅的解决方案.ADO.NET Entity Framework是.NET开发中一种 ...

  9. EF的三种模式

    1.DateBase First(数据库优先) 2.Model First(模型优先) 3.Code First(代码优先) 当然,如果把Code First模式的两种具体方式独立出来,那就是四种了. ...

随机推荐

  1. RH2288V3服务器 硬件安装以及更换硬件

    滑道类型:L型滑道.可伸缩滑道.报轨 L型滑道:只适用于华为机柜. 准备工具:防静电带或者防静电手套.螺丝刀.浮动螺母安装条. 安装服务器步骤: 1.安装L型滑道   2.安装浮动螺母  3.安装服务 ...

  2. 网络威胁实时地图(CyberThread Real-time Map)

    今天跟大家分享一下网络威胁实时地图(CyberThread Real-time Map),从地图上可以看出目前网络威胁情况数据. 点击打开网络威胁实时地图 可以点击demo on/off来看演示.可以 ...

  3. Spring的单例模式底层实现

    http://blog.csdn.net/cs408/article/details/48982085

  4. c# 小数四舍五入,向上取整,向下取整,见角进元保留多个小数位数

    /// <summary> /// 实现数据的四舍五入法 /// </summary> /// <param name="v">要进行处理的数据 ...

  5. Django-CRM项目学习(七)-权限组件的设置以及权限组件的应用

    开始今日份整理 1.利用自定制标签,增加展示权限,权限分级设定 1.1 在权限组件中创建自定义标签 使用自定义标签的目的,使各个数据进行分离 1.2 导入自定义标签包 自定义标签复习(自定义标签有三种 ...

  6. android glide图片加载框架

    项目地址: https://github.com/bumptech/glide Glide作为安卓开发常用的图片加载库,有许多实用而且强大的功能,那么,今天就来总结一番,这次把比较常见的都写出来,但并 ...

  7. identity server4 证书

    我们需要对token进行签名, 这意味着identity server需要一对public和private key. 幸运的是, 我们可以告诉identity server在程序的运行时候对这项工作进 ...

  8. leetcode 203. Remove Linked List Elements 、83. Remove Duplicates from Sorted List 、82. Remove Duplicates from Sorted List II(剑指offer57 删除链表中重复的结点)

    203题是在链表中删除一个固定的值,83题是在链表中删除重复的数值,但要保留一个:82也是删除重复的数值,但重复的都删除,不保留. 比如[1.2.2.3],83题要求的结果是[1.2.3],82题要求 ...

  9. 如何使用 IDEA 创建项目并且上传到 GitHub

    在 GitHub中 注册创建账号 :https://github.com 下载安装 Git : https://git-scm.com 安装成功后打开 Git Bash,输入下列命令,设置 Git 全 ...

  10. 解决java compiler level does not match the version of the installed java project facet【转载】

    原博文地址http://blog.csdn.net/chszs/article/details/8125828 Java compiler level does not match the versi ...