Entity Framework中的事务问题- 舊歡銣儚- 博客园

项目中试用Entity Framework1.0作为ORM层,但发现确实是问题多多,今天主要说一下其事务处理部分。

 

对于一般的单表操作,比如2个add操作要在一个事务完成,可以使用隐式事务,如:

 

TestEntities t=new TestEntities();

t.AddToTableA(t1);

t.AddToTableB(t2);

t.SaveChanges();

 

两句语句默认是在一个事务中提交的。

 

全是单表操作的话就按以上方式没有问题,但如果要使用存储过程,那么问题就来了。EF1.0对于存储过程支持并不好,凡是无返回值和返回值为标量的存储过程,使用Function Import之后,在cs文件里根本找不到对应的方法,要使用存储过程需要这么写:

 

    TestEntities t=new TestEntities();

    t.Connection.Open();

    var tran=t.Connection.BeginTransaction();

           EntityCommand cmd2 = new EntityCommand("TestEntities.UpdateForm", t.Connection as EntityConnection, tran as EntityTransaction);
            cmd2.CommandType = CommandType.StoredProcedure;
            cmd2.Parameters.Add(new EntityParameter(...));
            cmd2.Parameters.Add(new EntityParameter(...));
            cmd2.Parameters.Add(new EntityParameter(...));
            cmd2.Parameters[0].Value = ...
            cmd2.Parameters[1].Value =...

            cmd2.Parameters[2].Value =...

            cmd2.ExecuteNonQuery();

     tran.Commit();

 

此处的UpdateForm为存储过程名称,需要事先使用Function Import功能导入实体模型才能使用。

同时,这里的EntityTransaction事务可以和单表操作结合使用,比如以上代码可以变为

 

cmd2.ExecuteNonQuery();

t.AddToTable1(t1);

t.SaveChanges();

tran.Commit();

 

那么这个存储过程以及AddToTable1的操作是在一个事务内进行的,这是因为尽管SaveChanges()方法有自己的事务,但如果检测到t已经打开了现有的事务,那么会沿用已有事务。

 

但是这样写有一个麻烦,entity command只支持查询而不支持insert,update,delete,所以如果我要执行一句自定义语句update xxx set xxx where...就不行了。

 

EF Extension提供了一个扩展方法用于支持存储过程和自定义sql语句,用法如下:

 

var cmd = t.CreateStoreCommand("update form set xxx=xxx where formid=2", CommandType.Text);

var cmd = t.CreateStoreCommand("updateform", CommandType.StoredProcedure);

 

但是它使用的事务却要求是SqlTransaction,用以下的写法:

 

TestEntities t=new TestEntities();

t.Connection.Open();

EntityConnection con = t.Connection as EntityConnection;
var tran=con.StoreConnection.BeginTransaction();
var cmd = t.CreateStoreCommand("updateform", CommandType.StoredProcedure);

cmd.Transaction=tran;

cmd.ExecuteNonQuery();

tran.Commit();

 

注意这里的StoreConnection,使用它创建的就是SqlTransaction。这样的写法可以确保在存储过程和自定义语句之间使用事务,但如果你想将一个存储过程和一个单表操作放在一起,则又会出问题。

 

cmd.ExecuteNonQuery();

t.AddToTable1(t1);

t.SaveChanges();  =》这里会报错!

tran.Commit();

 

 

也就是说SaveChanges()方法只认EntityTransaction,而CreateStoreCommand只认SqlTransaction,两者无法统一。

 

所以如果要使用CreateStoreCommand方法,那么事务内相关的所有操作都要封装成存储过程形式,无法以单表操作进行调用;如果要使用EntityCommand+单表操作的事务,那么就要放弃自定义Sql语句,或将他们都写成存储过程的形式。

 

 

感谢韦恩卑鄙在评论中提供的解决方案:使用TransactionScope可以使之共存,示意代码如下:

 

using (var dbtran = new TransactionScope(TransactionScopeOption.Required))
            {

                using (var t= new TestEntities())
                {
                     using (t.Connection.CreateConnectionScope())// EF Extension中的扩展方法,作用是open connection并在dispose里释放它
                    {                     


                            t.AddToTableA(t1);

                            t.SaveChanges();
                            var cmd = t.CreateStoreCommand("update form set xxx=xxx where formid=2", CommandType.Text);

          cmd.ExcuteNonQuery();

                            dbtran.Complete();
                      }

      }

    }

 

如果所有操作都是本地事务,那么它将是一个轻型事务,不会用到MSDTC。如果是分布式的,则会自动升级为xx分布式事务,必须用到MSDTC。

 

 

Feedback

温景良(Jason)
用分布式事务的好处就是可以独立于数据层,在业务层实现组装事务。比如:
数据层方法

SaveOrder(); --保存订单
SaveOrderItem(); --保存订单明细
UpdateCustomer(); --更新用户信息
UpdateInventory(); --更新库存信息
SaveNetPay(); --保存支付信息


这些都是数据层基于ORM的独立映射方法

业务层

创建订单方法
CreateSo()
{
事务
{
SaveOrder(); --保存订单
SaveOrderItem(); --保存订单明细
UpdateCustomer(); --更新用户信息
UpdateInventory(); --更新库存信息
SaveNetPay(); --保存支付信息

}
}

这样可以以业务核心随意装配持久,如果上例不采用分布式事务,每个组装的业务都要实现相应的数据层聚合,想一想也比较可怕了。尤其以ORM为持久层的都是相对独立的数据映射。

温景良(Jason):
引用韦恩卑鄙 v-zhewg @waynebaby:
不需要 如果是同一个sqlconnection的话 自动调用sql 连接层的trans
不需要任何外部支持

如果是跨数据库的或者跨连接的,甚至跨服务的
TS会自动调用msdtc
你只要保证你的msdtc 服务开着 一般就可以了
万一中的万一 ,出错了可以再找资料 我帮你找呵呵

有个问题啊,如果在里面执行多条语句,数据层的连接是采用dispose(),这样会判断是同一个连接吗


只要context 不dispose 连接就还是同一个。

补充 看来是错的

只要是一次execute 就是同一个。。。
郑重声明:资讯 【Entity Framework中的事务问题- 舊歡銣儚- 博客园】由 发布,版权归原作者及其所在单位,其原创性以及文中陈述文字和内容未经(企业库qiyeku.com)证实,请读者仅作参考,并请自行核实相关内容。若本文有侵犯到您的版权, 请你提供相关证明及申请并与我们联系(qiyeku # qq.com)或【在线投诉】,我们审核后将会尽快处理。
—— 相关资讯 ——