背景:
从这里的另一个问题来看,我有一个Winforms解决方案(Finance),它包含许多项目(解决方案的固定项目)。现在,我的一个客户要求我“升级”解决方案,并添加来自另一个Winforms解决方案(HR)的项目/模块。
我真的不想把这些项目作为固定项目保留在现有的财务解决方案上。为此,我试图创建能够加载GUI、业务逻辑和数据层的插件,这些插件都使用MEF。
问题:
我有一个上下文(DbContext构建来实现模式),它有一个外部上下文列表(使用MEF加载--这些上下文表示来自每个插件的上下文,以及泛型存储库模式)。
假设我有这个:
public class MainContext : DbContext
{
public List<IPluginContext> ExternalContexts { get; set; }
// other stuff here
}和
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.
发布于 2012-01-06 22:42:46
如果使用多个上下文,每个上下文使用单独的连接,并且希望将数据保存到单个事务中的上下文中,则必须在分布式事务中使用TransactionScope。
您的链接问题不是这种情况,因为在这种情况下,首先连接不修改数据,因此可以在数据被修改的连接启动之前关闭它。在您的示例中,数据在多个连接上被并发修改,这需要两阶段提交和MSDTC。
您可以尝试通过在多个上下文之间共享单个连接来解决这个问题,但这可能非常棘手。我不确定以下示例是否可靠,但您可以尝试一下:
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();
}
}上下文构造函数定义为:
public Context(DbConnection connection) : base(connection,false) { }该示例本身对我有效,但它存在多个问题:
Transaction.Current具有空的分布式事务Id,因此应该仍然是本地的。ObjectContext,因为DbContext没有所有必要的方法。H 214H 115它不需要在每个场景中工作。甚至MSDN也宣称:当连接在单个事务中关闭并重新打开时,可能会发生事务对DTC的
提升。由于实体框架自动打开和关闭连接,因此应考虑手动打开和关闭连接,以避免事务升级。
DbContext API的问题是,即使您手动打开它,它也会关闭并重新打开连接,因此,如果它始终正确地标识它是否在事务上下文中运行而不关闭连接,则这是一个打开的问题。
发布于 2012-01-17 14:38:48
@Ladislav Mrnka你从一开始就说对了:我必须使用MSDTC。
我在这里尝试过多种方法,包括我提供的示例代码。我已经用变了的野兔测试了很多次,但是它不起作用。这个错误深入到EF和DbContext的工作方式中,为了改变这一点,我最终发现自己使用了自己的ORM工具。不是这样的。
我也和一个知道EF的朋友(MVP)谈过。我们已经在这里测试了一些其他的东西,但它不能按照我所希望的方式工作。最后,我将得到多个独立的事务(我试图将它们与我的示例代码结合在一起),使用这种方法,我无法自动执行完整的回滚,我将不得不创建大量的通用/自定义代码来手动回滚更改,下面是另一个问题:如果这种回滚失败了(这不是回滚,只是更新)?
因此,我们在这里找到的唯一方法是使用MSDTC并构建一些工具来帮助调试/测试DTC是否启用、客户机/服务器防火墙是否正常以及所有这些东西。
不管怎样,谢谢你。=)
发布于 2012-10-19 19:32:29
那么,到10月19日,这种情况有可能发生改变吗?在整个管道中,人们建议使用以下代码,但它不起作用:
(_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();这对于跨存储库实现单个工作单元类是一个严重的拖累。有什么新办法吗?
https://stackoverflow.com/questions/8763248
复制相似问题