两个嵌套的Entity Framework上下文,共享一个事务

时间:2018-08-13 21:09:54

标签: c# sql-server transactions entity-framework-6

我有类似以下示例的代码。由于需要通过SP完成一些数据库伪造,并且其中包含一个保存更改,因此涉及到显式事务。 (省略了异常处理,回滚等。)

void OuterMethod(MyDatbase context)
{
    using(var dbTrans = context.Database.BeginTransaction())
    {
        // some stuff, the save puts the data where the SP can see it
        Stuff(context);
        context.SaveChanges();

        // now some SP stuff
        context.Database.ExecuteSqlCommand(@"spFoo", params);

        // more stuff
        MoreStuff(context);
        AlmostUnrelatedCode(context);
        context.SaveChanges();

        dbTrans.Commit();
    }
}

现在,方法AlmostUnrelatedCode()(仅与上述过程略有关联)在99%的时间内需要一个良好,快速,可处理的只读上下文。我有一家工厂,可以在需要时为我提供合适的服务。从上方该块的中间被调用的时间为1%。

MyDatabase localReadOnlyContext;

void AlmostUnrelatedCode(MyDatabase context)
{
    if ( context.Database.CurrentTransaction != null )
    {
        // Must use the context passed or everything deadlocks  :(
        localReadOnlyContext = context;
        disposeContextLater = false;
    }
    else
    {
        // I just want to do this all the time
        localReadOnlyContext = _contextFactory.CreateReadOptimized();
        disposeContextLater = true;
    }

    // Do many, many things with my read-optimized context...

    // The Dispose() on the class will check for disposeContextLater
}

我想做的是摆脱交易检查,实际上,如果我能帮助的话,根本不需要传递外部上下文。

我尝试过的事情:

  • 只需忽略外部事务中发生的一切,并始终使用生成的上下文。问题:僵局。

  • 尝试将最外面的事务放入我用_contextFactory创建的EF上下文中。问题:EF上下文构造函数不允许您传递现有事务。 Database.CurrentTransaction也没有二传手。

  • 将整个事务拉到TransactionScope中,将所有内容打包。问题:方法OuterMethod在上下文中传递 ,而我无法控制调用者。

我无法尝试的事情:

  • 脏读/锁定。 AlmostUnrelatedCode()需要到目前为止所写的数据。

我不想:

  • 只需在AlmostUnrelatedCode内部使用外部上下文即可。 AlmostUnrelatedCode处理大量数据树,并且上下文很快变得发胖和不快乐。它确实会很快用废话污染其上下文,我宁愿在完成后将其丢弃。

2 个答案:

答案 0 :(得分:1)

您可以通过将一个连接用于多个上下文来防止死锁。

示例

     var efConnectionString = ConfigurationManager.ConnectionStrings["SomeEntities"].ConnectionString;
    // note EntityConnection, not SqlConnection
    using (var conn = new EntityConnection(efConnectionString)) {
        // important to prevent escalation
        await conn.OpenAsync();
        using (var c1 = new SomeEntities(conn, contextOwnsConnection: false)) {
            //Use some stored procedures etc.
            count1 = await c1.SomeEntity1.CountAsync();
        }

        using (var c2 = new SomeEntities(conn, contextOwnsConnection: false)) {
            //Use some stored procedures etc.
            count2 = await c2.SomeEntity21.CountAsync();
        }
    }

在您的情况下,只需从上下文获取连接并重复使用

context.Database.Connection

答案 1 :(得分:1)

您不能像这样将在AlmostUnrelatedCode中完成的事情分开:

void AlmostUnrelatedCode()
{
   var context = _contextFactory.CreateReadOptimized();
   AlmostUnrelatedCode(context);
   context.Dispose();
}

void AlmostUnrelatedCode(MyDatabase context)
{
    // Do many, many things with context...
}

现在,您可以从AlmostUnrelatedCode(with param)致电OuterMethod。也许还有更多的东西需要分离。考虑 S OLID。