【问题标题】:Breeze - EF, TransactionScope and the open connection in BeforeSaveEntitiesBreeze - EF、TransactionScope 和 BeforeSaveEntities 中的打开连接
【发布时间】:2014-08-07 03:25:38
【问题描述】:

通过查看 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);
}

【问题讨论】:

    标签: entity-framework transactions breeze


    【解决方案1】:

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

    解决方案是使用相同的 DbConnection 创建您的第二个 DbContext。 EFContextProviderEntityConnection 属性允许这样做:

    protected override Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(
        Dictionary<Type, List<EntityInfo>> saveMap) 
    {
        var secondContext = new MyDbContext(EntityConnection);
        // use secondContext for business rule validations,
        // e.g. compare new values to existing values
    }
    

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

    This SO post 详细介绍了 Breeze 如何创建 DbContext。

    【讨论】:

      【解决方案2】:

      不完全确定我了解您的用例或为什么会进行 DTC 推广。如果您使用完全相同的连接字符串并且使用的是最新版本的 SQL Server 或 Oracle,则不应发生 DTC 升级(注意:SQL Server 2005 等较旧的数据库确实存在一些意外 DTC 升级的问题)。或者我只是不明白你的问题。

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

      【讨论】:

        猜你喜欢
        • 2010-12-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-26
        • 2023-03-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多