如何强制在多个DbContext类中只有一个事务? [英] How to force only one transaction within multiple DbContext classes?

查看:424
本文介绍了如何强制在多个DbContext类中只有一个事务?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景:

从另一个问题,在这里,我有一个Winforms解决方案(财务)与许多项目(解决方案的固定项目)。
现在我的一位客户要求我升级解决方案,并添加另一个Winforms解决方案(HR)的项目/模块。

Background:
From another question here at SO I have a Winforms solution (Finance) with many projects (fixed projects for the solution). Now one of my customers asked me to "upgrade" the solution and add projects/modules that will come from another Winforms solution (HR).

我真的不想将这些项目作为现有融资解决方案的固定项目。为此,我正在尝试创建插件,将加载GUI,业务逻辑和数据层都使用MEF。

I really don't want to keep these projects as fixed projects on the existing finance solution. For that I'm trying to create plugins that will load GUI, business logic and the data layer all using MEF.

问题:

我有一个上下文(DbContext构造来隐含通用存储库模式)与外部上下文列表(使用MEF加载 - 这些上下文表示每个插件的上下文,也包含通用存储库模式)。

Question:
I have a context (DbContext built to implment the Generic Repository Pattern) with a list of external contexts (loaded using MEF - these contexts represent the contexts from each plugin, also with the Generic Repository Pattern).

假设我有这个:

public class MainContext : DbContext
{
   public List<IPluginContext> ExternalContexts { get; set; }

   // other stuff here
}

/ p>

and

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

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

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

and within the MainContext class, already loaded, I have both external contexts (from plugins).

考虑到这一点,假设我有一个会影响到的事务

With that in mind, let's say I have a transaction that will impact both the MainContext and the PluginContext_B.

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

使用IUnityOfWork我可以为最后一个项目设置SaveChanges(),但据我所知,我必须有一个单独的上下文作为一个单一的交易工作。

Using the IUnityOfWork I can set the SaveChanges() for the last item but as far as I know I must have a single context for it to work as a single transaction.

有一种使用MSDTC(TransactionScope)的方法,但这种方法是可怕的,我会rea没有使用这个(也是因为我需要在客户端和服务器上启用MSDTC,并且我一直有崩溃和泄漏)。

There's a way using the MSDTC (TransactionScope) but this approach is terrible and I'd reather not use this at all (also because I need to enable MSDTC on clients and server and I've had crashes and leaks all the time).

更新:

系统正在使用SQL 2008 R2。

如果可以以不扩展到MSDTC的方式使用TransactionScope,那么我从来没有实现过。我一直使用TransactionScope进入MSDTC。根据SO的另一篇文章,有些情况下,TS不会进入MSDTC:查看这里。但是我真的宁愿使用其他方式,而不是TransactionScope ...

Update:
Systems are using SQL 2008 R2. Never bellow.
If it's possible to use TransactionScope in a way that won't scale to MSDTC it's fine, but I've never achieved that. All the time I've used TransactionScope it goes into MSDTC. According to another post on SO, there are some cases where TS will not go into MSDTC: check here. But I'd really prefer to go into some other way instead of TransactionScope...

推荐答案

如果您正在使用多个上下文使用单独的连接,并且您希望在单个事务中将数据保存到这些上下文中,您必须使用分布式事务(MSDTC)的 TransactionScope

If you are using multiple contexts each using separate connection and you want to save data to those context in single transaction you must use TransactionScope with distributed transaction (MSDTC).

您的链接问题不是这种情况,因为在这种情况下,第一个连接不会修改数据,因此可以在开始修改数据的连接之前关闭数据。在您的情况下,数据同时在多连接上进行修改,需要两阶段提交和MSDTC。

Your linked question is not that case because in that scenario first connection do not modify data so it can be closed prior to starting the connection where data are modified. In your case data are concurrently modified on multiple connection which requires two-phase commit and MSDTC.

您可以尝试通过在多个上下文之间共享单个连接来解决此问题,但可以相当棘手我不知道以下示例是多么可靠,但可以尝试一下:

You can try to solve it with sharing single connection among multiple contexts but that can be quite tricky. I'm not sure how reliable the following sample is but you can give it a try:

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();
    }
}

上下文构造函数定义为:

Context constructor is defined as:

public Context(DbConnection connection) : base(connection,false) { }

样本本身适用于我,但它有多个问题:

The sample itself worked for me but it has multiple problems:


  • 首次使用上下文必须用闭合连接完成。这就是为什么我在打开连接之前添加实体的原因。

  • 我在交易之外手动打开连接,但也可能不需要。

  • 两个保存更改都成功运行, Transaction.Current 具有空的分布式事务ID,所以它应该仍然是本地的。

  • 保存要复杂得多,你必须使用 ObjectContext 因为 DbContext 没有所有必要的方法。

  • 它不必在每个场景。即使MSDN声称:

  • First usage of contexts must be done with closed connection. That is the reason why I'm adding entities prior to opening the connection.
  • I rather open connection manually outside of the transaction but perhaps it is not needed.
  • Both save changes successfully run and Transaction.Current has empty distributed transaction Id so it should be still local.
  • The saving is much more complicated and you must use ObjectContext because DbContext doesn't have all necessary methods.
  • It doesn't have to work in every scenario. Even MSDN claims this:

当连接是$ b时,可能会向DTC推广交易$ b在单个交易中关闭并重新打开。因为实体
框架自动打开并关闭连接,所以您应该
考虑手动打开和关闭连接,以避免
事务升级。

Promotion of a transaction to a DTC may occur when a connection is closed and reopened within a single transaction. Because the Entity Framework opens and closes the connection automatically, you should consider manually opening and closing the connection to avoid transaction promotion.

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

The problem with DbContext API is that it closes and reopens connection even if you open it manually so it is a opened question if API always correctly identifies if it runs in the context of transaction and do not close connection.

这篇关于如何强制在多个DbContext类中只有一个事务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆