EF6学习笔记二十四:事务

要专业系统地学习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

View Code


//  ado中使用事务
string connStr = @"Data Source=LAPTOP-G81QJ856\SQLEXPRESS;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;  
            }
        }
    }
}

View Code

现在来看看EF中事务是怎么回事

我们平时在执行添加、修改、删除调用SaveChanges方法时,就会被默认的被事务包裹。查询没有


ctx.Students.Add(new Student { Name = "小刘", Score = "55", AddTime = DateTime.Now });

View Code

 当我们多次调用SaveChanges时,就会开启多个事务。


ctx.Students.Add(new Student { Name="小新",Score="66"});
ctx.SaveChanges();
ctx.Teachers.Add(new Teacher { Name="胡飘",Subject="历史"});
ctx.SaveChanges();

View Code

现在来看EF中的一个关于事务的配置


public class EFDbContext : DbContext
    {
        public EFDbContext()
        {
            //此标志确定在使用此类方法时是否启动新事务
            Configuration.EnsureTransactionsForFunctionsAndCommands = false;
        }
}

View Code

这个就是开启关闭事务的开关,但是他对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();

View Code

ExecuteSqlCommand也是默认被事务包裹,但是他就可以通过传递参数来实现事务的开启和关闭。


ctx.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction, @"insert into tb_students values(newid(),'小刘','70',getdate())");

View Code

行吧,就是这样。

如果说我们想用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();
}

View Code

 我虽然调用了两次saveChanges,但只有一个事务,但是必须要调用一次SaveChanges才能成功插入

最后开看一下EF提供的UseTransaction方法,这个是什么意思呢?允许上下文参与到已存在的事务中。

我们现在使用ADO,将ADO的transaction对象传递给EF的UseTransaction,那么我在ADO中的操作和在EF中的操作就可以处在同一个事务中


string connStr = @"Data Source=LAPTOP-G81QJ856\SQLEXPRESS;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;
            }
        }
    }
}

View Code

 

 行吧,EF中简单的事务就到这里了。