zoukankan      html  css  js  c++  java
  • 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。如果是分布式的,则会自动升级为完全分布式事务,必须用到MSDTC。

  • 相关阅读:
    [转]iOS应用程序多语言本地化解决方案
    【汇】iOS面试题
    [转]UIView 和 CALayer的那点事
    [转]25个增强iOS应用程序性能的提示和技巧 — 高级篇
    [转]25个增强iOS应用程序性能的提示和技巧 — 中级篇
    [转]25个增强iOS应用程序性能的提示和技巧 — 初级篇
    [转]NSNotification、delegate和KVO、KVC的区别
    [转]ViewController的生命周期
    [QualityCenter]设置工作流脚本-设置不同字段值关联不同列表
    [QualityCenter]设置工作流脚本-缺陷字段值发生变化时的处理
  • 原文地址:https://www.cnblogs.com/hgamezoom/p/1777227.html
Copyright © 2011-2022 走看看