首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在多个DbContext类中强制只处理一个事务?

如何在多个DbContext类中强制只处理一个事务?
EN

Stack Overflow用户
提问于 2012-01-06 19:21:25
回答 4查看 6K关注 0票数 2

背景:

从这里的另一个问题来看,我有一个Winforms解决方案(Finance),它包含许多项目(解决方案的固定项目)。现在,我的一个客户要求我“升级”解决方案,并添加来自另一个Winforms解决方案(HR)的项目/模块。

我真的不想把这些项目作为固定项目保留在现有的财务解决方案上。为此,我试图创建能够加载GUI、业务逻辑和数据层的插件,这些插件都使用MEF。

问题:

我有一个上下文(DbContext构建来实现模式),它有一个外部上下文列表(使用MEF加载--这些上下文表示来自每个插件的上下文,以及泛型存储库模式)。

假设我有这个:

代码语言:javascript
复制
public class MainContext : DbContext
{
   public List<IPluginContext> ExternalContexts { get; set; }

   // other stuff here
}

代码语言:javascript
复制
public class PluginContext_A : DbContext, IPluginContext
{ /* Items from this context */ }

public class PluginContext_B : DbContext, IPluginContext
{ /* Items from this context */ }

在已经加载的MainContext类中,我有两个外部上下文(来自插件)。

考虑到这一点,假设我有一个同时影响MainContext和PluginContext_B的事务。

如何在一个事务中对两个上下文执行更新/插入/删除(工作的统一)?

使用IUnityOfWork,我可以为最后一项设置SaveChanges(),但据我所知,它必须有一个上下文才能作为单个事务工作。

有一种使用MSDTC (TransactionScope)的方法,但是这种方法很糟糕,我根本不会使用它(还因为我需要在客户端和服务器上启用MSDTC,并且经常会发生崩溃和泄漏)。

更新:

系统正在使用SQL 2008 R2。永远不要咆哮。

如果有可能以一种不会扩展到MSDTC的方式使用TransactionScope,那很好,但我从未实现过。一直以来,我都在使用TransactionScope,它进入了MSDTC。根据SO上的另一篇文章,有些情况下TS不会进入MSDTC:check here。但我更愿意走别的路,而不是TransactionScope.

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-01-06 22:42:46

如果使用多个上下文,每个上下文使用单独的连接,并且希望将数据保存到单个事务中的上下文中,则必须在分布式事务中使用TransactionScope

您的链接问题不是这种情况,因为在这种情况下,首先连接不修改数据,因此可以在数据被修改的连接启动之前关闭它。在您的示例中,数据在多个连接上被并发修改,这需要两阶段提交和MSDTC。

您可以尝试通过在多个上下文之间共享单个连接来解决这个问题,但这可能非常棘手。我不确定以下示例是否可靠,但您可以尝试一下:

代码语言:javascript
复制
using (var connection = new SqlConnection(connnectionString))
{
    var c1 = new Context(connection);
    var c2 = new Context(connection);

    c1.MyEntities.Add(new MyEntity() { Name = "A" });
    c2.MyEntities.Add(new MyEntity() { Name = "B" });

    connection.Open(); 

    using (var scope = new TransactionScope())
    {
        // This is necessary because DbContext doesnt't contain necessary methods
        ObjectContext obj1 = ((IObjectContextAdapter)c1).ObjectContext;
        obj1.SaveChanges(SaveOptions.DetectChangesBeforeSave);

        ObjectContext obj2 = ((IObjectContextAdapter)c2).ObjectContext;
        obj2.SaveChanges(SaveOptions.DetectChangesBeforeSave);

        scope.Complete();

        // Only after successful commit of both save operations we can accept changes
        // otherwise in rollback caused by second context the changes from the first
        // context will be already accepted = lost

        obj1.AcceptAllChanges();
        obj2.AcceptAllChanges();
    }
}

上下文构造函数定义为:

代码语言:javascript
复制
public Context(DbConnection connection) : base(connection,false) { }

该示例本身对我有效,但它存在多个问题:

  • 第一次使用上下文必须使用封闭连接。这就是我在打开连接之前添加实体的原因。
  • I在事务之外手动打开连接,但可能不需要。
  • 都成功地保存更改,Transaction.Current具有空的分布式事务Id,因此应该仍然是本地的。
  • 保存要复杂得多,您必须使用ObjectContext,因为DbContext没有所有必要的方法。H 214H 115它不需要在每个场景中工作。甚至MSDN也宣称:

当连接在单个事务中关闭并重新打开时,可能会发生事务对DTC的

提升。由于实体框架自动打开和关闭连接,因此应考虑手动打开和关闭连接,以避免事务升级。

DbContext API的问题是,即使您手动打开它,它也会关闭并重新打开连接,因此,如果它始终正确地标识它是否在事务上下文中运行而不关闭连接,则这是一个打开的问题。

票数 3
EN

Stack Overflow用户

发布于 2012-01-17 14:38:48

@Ladislav Mrnka你从一开始就说对了:我必须使用MSDTC。

我在这里尝试过多种方法,包括我提供的示例代码。我已经用变了的野兔测试了很多次,但是它不起作用。这个错误深入到EF和DbContext的工作方式中,为了改变这一点,我最终发现自己使用了自己的ORM工具。不是这样的。

我也和一个知道EF的朋友(MVP)谈过。我们已经在这里测试了一些其他的东西,但它不能按照我所希望的方式工作。最后,我将得到多个独立的事务(我试图将它们与我的示例代码结合在一起),使用这种方法,我无法自动执行完整的回滚,我将不得不创建大量的通用/自定义代码来手动回滚更改,下面是另一个问题:如果这种回滚失败了(这不是回滚,只是更新)?

因此,我们在这里找到的唯一方法是使用MSDTC并构建一些工具来帮助调试/测试DTC是否启用、客户机/服务器防火墙是否正常以及所有这些东西。

不管怎样,谢谢你。=)

票数 0
EN

Stack Overflow用户

发布于 2012-10-19 19:32:29

那么,到10月19日,这种情况有可能发生改变吗?在整个管道中,人们建议使用以下代码,但它不起作用:

代码语言:javascript
复制
    (_contextA as IObjectContextAdapter).ObjectContext.Connection.Open();
    (_contextB as IObjectContextAdapter).ObjectContext.Connection.Open();

    using (var transaction = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions{IsolationLevel = IsolationLevel.ReadUncommitted, Timeout = TimeSpan.MaxValue}))
{
    _contextA.SaveChanges();
    _contextB.SaveChanges();

    // Commit the transaction
    transaction.Complete();
}

    // Close open connections
    (_contextA as IObjectContextAdapter).ObjectContext.Connection.Close();
    (_contextB as IObjectContextAdapter).ObjectContext.Connection.Close();

这对于跨存储库实现单个工作单元类是一个严重的拖累。有什么新办法吗?

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8763248

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档