一. 什么是事务

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

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

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. WPF中自定义标题栏时窗体最大化处理之WindowChrome

    注意: 本文方法基础是WindowChrome,而WindowChrome在.NET Framework 4.5之后才集成发布的.见:WindowChrome Class 在.NET Framewor ...

  2. 多线程中的event,用于多线程的协调

    ''' 简单的需求:红绿灯,红灯停,绿灯行 一个线程扮演红绿灯,每过一段时间灯变化,3-5个线程扮演车,红灯停,绿灯行 红绿灯线程和车的线程会相互依赖 这种场景怎么实现?---事件 切换一次灯就是一次 ...

  3. spring mybatics

    spring boot     web.mysql.mybatics.jps 遇到jar包版本不行的直接换个版本Add进去 https://github.com/forezp/SpringBootLe ...

  4. Hive:ORC File Format存储格式详解

    一.定义 ORC File,它的全名是Optimized Row Columnar (ORC) file,其实就是对RCFile做了一些优化. 据官方文档介绍,这种文件格式可以提供一种高效的方法来存储 ...

  5. FeathersJS简单使用指南,一个前端也能玩得转的后端框架

    官方文档 :https://docs.feathersjs.com/api/services.html#createdata-params 第一步,创建接口和服务 app.use("user ...

  6. js判断数组中有没有指定元素

    list.findIndex(target => target.sel === 指定元素) ,如果找到返回下标,如果没有返回-1

  7. Activiti6作业执行器Job Executor配置(学习笔记)

    内容概况: 异步执行配置相关: asyncExecutorActivate:这个属性是激活作业执行器,它的默认参数是false,只有设为true,activiti启动的时候才会开启线程池去扫描定时操作 ...

  8. SpringCloud学习笔记:服务注册与发现Eureka(2)

    1. Eureka简介 Eureka是一个用于服务注册和发现的组件,分为Eureka Server和Eureka Client,Eureka Server为Eureka服务注册中心,Eureka Cl ...

  9. css3多個佈局

    分欄佈局: column-count:分欄數 column-gap:欄間間距 column-rule:欄間線條 記得消除瀏覽器兼容:-moz-和-webkit- 盒佈局: display:box 彈性 ...

  10. SpringMVC DispatcherServlet在配置Rest url-pattern的一点技巧

    SpringMVC的Controller中已经有了@RequestMapping(value = "detail.do", method = RequestMethod.GET)的 ...