要专业系统地学习EF推荐《你必须掌握的Entity Framework 6.x与Core 2.0》。这本书作者(汪鹏,Jeffcky)的博客:https://www.cnblogs.com/CreateMyself/
了解一下EF中事务。首先复习一下原始的SQL语句创建事务,然后也复习一下ADO中事务的使用。工作一年多我就没写过事务,因为用的是EF,事务就只是在学校中写过。
详细学习事务:https://www.cnblogs.com/knowledgesea/p/3714417.html
begin transaction begin try insert into tb_Students values(newid(),'小明','44','2012-1-1'); insert into tb_Teachers values(newid(),'张四海','语文','2018-2-2'); end try begin catch select ERROR_NUMBER() as ErrorNumber, -- 错误代码 ERROR_SEVERITY() as ErrorSeverity,-- 错误严重级别,级别小于10,try catch捕获不到 ERROR_STATE() as ErrorState, -- 错误状态码 ERROR_PROCEDURE() as ErrorProcedure, -- 出现错误的存储过程或触发器的名称 ERROR_LINE() as ErrorLine, -- 发生错误的行号 ERROR_MESSAGE() as ErrorMessage -- 错误的具体信息 if(@@TRANCOUNT > 0) -- 事务开启此值+1,判断是否开启事务 rollback transaction end catch if(@@TRANCOUNT > 0) commit tran
// ado中使用事务 string connStr = @"Data Source=LAPTOP-G81QJ856SQLEXPRESS;Initial Catalog=_20190130.EFDbContext;Integrated Security=True"; using (SqlConnection conn = new SqlConnection(connStr)) { Console.WriteLine(conn.State); // closed 我还以为使用using自动打开的 conn.Open(); string sql = @"insert into tb_Students values(newid(),'小强','44','2012-1-1');insert into tb_Teachers values(newid(),'张抛','数学','1999-9-2');"; using (SqlCommand cmd = new SqlCommand(sql, conn)) { using (SqlTransaction tran = conn.BeginTransaction()) { try { // 如果不告诉cmd使用哪个事务会报错 //// System.InvalidOperationException: 如果分配给命令的连接位于本地挂起事务中,ExecuteNonQuery 要求命令拥有 事务。命令的 Transaction 属性尚未初始化。 cmd.Transaction = tran; var i = cmd.ExecuteNonQuery(); tran.Commit(); } catch (Exception e) { tran.Rollback(); throw e; } } } }
现在来看看EF中事务是怎么回事
我们平时在执行添加、修改、删除调用SaveChanges方法时,这些操作默认就会被事务包裹。查询操作则没有。
ctx.Students.Add(new Student { Name = "小刘", Score = "55", AddTime = DateTime.Now });
当我们多次调用SaveChanges时,就会开启多个事务。
ctx.Students.Add(new Student { Name="小新",Score="66"}); ctx.SaveChanges(); ctx.Teachers.Add(new Teacher { Name="胡飘",Subject="历史"}); ctx.SaveChanges();
现在来看EF中的一个关于事务的开关
public class EFDbContext : DbContext { public EFDbContext() { //此标志确定在使用此类方法时是否启动新事务 Configuration.EnsureTransactionsForFunctionsAndCommands = false; } }
这个就是开启关闭事务的开关,但是他对SaveChanges是不起作用的,目前我只知道调用ExecuteSqlCommand方式时才有用。MSDN上对这个说的很简陋
那么看看EF中对这个属性的注释
还是直接使用它来看看。下面这条语句我关闭事务, 添加一条记录,然后调用SaveChanges,但其实,还是会开启事务
// 还是会有事务的 ctx.Configuration.EnsureTransactionsForFunctionsAndCommands = false; ctx.Database.Log = msg => Console.WriteLine(msg); ctx.Teachers.Add(new Teacher { Name = "胡愤", Subject = "历史" }); ctx.Students.Add(new Student { Name = "小赵", Score = "66", AddTime = DateTime.MinValue }); ctx.SaveChanges();
ExecuteSqlCommand也是默认被事务包裹,但是他就可以通过传递参数来实现事务的开启和关闭。
ctx.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, @"insert into tb_students values(newid(),'小刘','70',getdate())");
行吧,就是这样。
如果说我们想用ADO中那种方式来使用事务呢?当然是可以的,上下文提供了BeginTransaction,和ADO中的命名是一样的。
using (var transaction = ctx.Database.BeginTransaction()) { ctx.Teachers.Add(new Teacher { Name = "胡愤", Subject = "历史" }); ctx.SaveChanges(); ctx.Students.Add(new Student { Name = "小赵", Score = "66", AddTime = DateTime.Now }); ctx.SaveChanges(); transaction.Commit(); }
我虽然调用了两次saveChanges,但只有一个事务,但是必须要调用一次SaveChanges才能成功插入
最后开看一下EF提供的UseTransaction方法,这个是什么意思呢?允许上下文参与到已存在的事务中。
我们现在使用ADO,将ADO的transaction对象传递给EF的UseTransaction,那么我在ADO中的操作和在EF中的操作就可以处在同一个事务中
string connStr = @"Data Source=LAPTOP-G81QJ856SQLEXPRESS;Initial Catalog=_20190130.EFDbContext;Integrated Security=True"; using (SqlConnection conn = new SqlConnection(connStr)) { conn.Open(); string sql = @"insert into tb_students values(newid(),'小蓝 ','70',getdate())"; using (SqlCommand cmd = new SqlCommand(sql, conn)) { using (SqlTransaction tran = conn.BeginTransaction()) { try { cmd.Transaction = tran; var i = cmd.ExecuteNonQuery(); using (EFDbContext ctx = new EFDbContext(conn)) { ctx.Database.UseTransaction(tran); ctx.Students.Add(new Student { Name = "小红", Score = "55", AddTime = DateTime.Now }); ctx.SaveChanges(); } tran.Commit(); } catch (Exception e) { tran.Rollback(); throw e; } } } }
行吧,EF中简单的事务就到这里了。