Breeze - EF,TransactionScope和BeforeSaveEntities中的开放连接

时间:2014-08-06 14:35:37

标签: entity-framework transactions breeze

通过查看Breeze源代码,我看到ContextProvider在最终调用BeforeSaveEntities之前打开了一个连接。

问题:我想知道是什么原因造成的?因为在某些情况下,这会导致不必要的交易促销。解释如下。

ContextProvider.cs:

private void OpenAndSave(SaveWorkState saveWorkState) {      
  OpenDbConnection();    // ensure connection is available for BeforeSaveEntities
  saveWorkState.BeforeSave();
  SaveChangesCore(saveWorkState);
  saveWorkState.AfterSave();
}

EFContextProvider.cs:

protected override void OpenDbConnection() {
  var ec = ObjectContext.Connection as EntityConnection;
  if (ec.State == ConnectionState.Closed) ec.Open();
}

如果我们在事务中保存,连接已经打开的事实会导致事务被提升为分布式事务,如果在BeforeSaveEntities中我们创建另一个DbContext以在数据库中执行其他一些保存操作(例如审计或我们想要做的任何其他工作)并尝试将其与当前交易一起使用。

澄清我想说的话:

1)在非微风的情况下,以下代码不会将事务提升为分布式事务:

using(TransactionScope ts1 = new TransactionScope(TransactionScopeOption.Required))
{
  using(TransactionScope ts2 = new TransactionScope(TransactionScopeOption.Required))
  {
    MyContext2.SaveChanges(); /* Opens & closes a connection */
    ts2.Complete();
  }
  MyContext1.SaveChanges(); /* Opens & closes a connection */
  ts1.Complete();
}

2)有了微风,如果我总结一下代码的操作顺序,我们得到:

// Breeze opens the connection
OpenDbConnection(); /* Opens a connection for the breeze context */
// Breeze creates a TransactionScope
using(TransactionScope ts1 = new TransactionScope(TransactionScopeOption.Required))
{
  // BeforeSaveEntities is called
  // In BeforeSaveEntities, we decide to create/update/delete some other entities,
  // but want these operations to be part of the same transaction.
  // So, we create our own context, do our work and save it with a TransactionScope.
  using(TransactionScope tsInBeforeSaveEntities = new TransactionScope(TransactionScopeOption.Required))
  {
    // ISSUE IS HERE:
    // The following code with cause the transaction to be promoted to a 
    // distributed transaction, because another connection is already open by breeze.
    MyContextInBeforeSaveEntities.SaveChanges(); /* Opens & closes a connection */
    tsInBeforeSaveEntities.Complete();
  }
  // BeforeSaveEntities terminates
  // Breeze saves the changes & complete its transaction.
  [BreezeContext].SaveChanges(); /* Uses the already open connection */
  ts1.Complete();
}

如果Breeze在调用BeforeSaveEntities之前没有调用OpenDbConnection(),我们就不会遇到提升事务的问题。

我阻止事务处理的解决方法是在覆盖BeforeSaveEntities时关闭并重新打开Breeze的连接,但这有点令人讨厌。

protected override bool BeforeSaveEntity(EntityInfo entityInfo)
{
  ObjectContext.Connection.Close();

  // Create my DbContext, my TransactionScope and call SaveChanges

  ObjectContext.Connection.Open();

  return base.BeforeSaveEntity(entityInfo);
}

2 个答案:

答案 0 :(得分:1)

如果使用默认构造函数创建新的DbContext,它将创建一个新的DbConnection。由于您在同一时间有两个数据库连接在飞行中,这是导致TransactionScope将其提升为分布式事务的原因。

解决方案是使用相同的DbConnection创建第二个DbContext。 EntityConnection的{​​{1}}属性允许这样做:

EFContextProvider

这样,使用了相同的底层DbConnection,因此不会发生分布式事务。

This SO post更多关于Breeze如何创建DbContexts。

答案 1 :(得分:0)

我不完全确定我了解您的用例或DTC促销活动的原因。如果您使用完全相同的连接字符串并使用最新版本的SQL Server或Oracle,则不应进行DTC升级(注意:SQL Server 2005等旧版数据库确实存在意外DTC促销的一些问题)。或者我可能只是不明白你的问题。

在保存操作开始之前创建和打开连接的合理性是确保在BeforeSave和AfterSave方法中完成的任何“额外”工作都是与保存的其余部分相同的事务的一部分。这是非常慎重的。但是,也许我错过了你的观点。