Entity framework Breeze-EF、TransactionScope和BeforeSaveEntities中的开放连接

Entity framework Breeze-EF、TransactionScope和BeforeSaveEntities中的开放连接,entity-framework,transactions,breeze,Entity Framework,Transactions,Breeze,通过查看Breeze源代码,我看到ContextProvider在最终调用BeforeSaveEntities之前打开了一个连接 问:我想知道原因是什么?因为在某些情况下,这将导致不必要的事务升级。解释如下 ContextProvider.cs: private void OpenAndSave(SaveWorkState saveWorkState) { OpenDbConnection(); // ensure connection is available for B

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

问:我想知道原因是什么?因为在某些情况下,这将导致不必要的事务升级。解释如下

ContextProvider.cs:

private void OpenAndSave(SaveWorkState saveWorkState) {      
  OpenDbConnection();    // ensure connection is available for BeforeSaveEntities
  saveWorkState.BeforeSave();
  SaveChangesCore(saveWorkState);
  saveWorkState.AfterSave();
}
protected override void OpenDbConnection() {
  var ec = ObjectContext.Connection as EntityConnection;
  if (ec.State == ConnectionState.Closed) ec.Open();
}
EFContextProvider.cs:

private void OpenAndSave(SaveWorkState saveWorkState) {      
  OpenDbConnection();    // ensure connection is available for BeforeSaveEntities
  saveWorkState.BeforeSave();
  SaveChangesCore(saveWorkState);
  saveWorkState.AfterSave();
}
protected override void OpenDbConnection() {
  var ec = ObjectContext.Connection as EntityConnection;
  if (ec.State == ConnectionState.Closed) ec.Open();
}
如果在事务中保存,连接已打开的事实会导致事务升级为分布式事务,如果在BeforeSaveEntities中创建另一个DbContext以在数据库中执行其他保存操作(如审核或任何其他我们想要执行的工作)并尝试将其登记到当前事务中

为了澄清我想说的话:

1) 在非breeze场景中,以下代码不会将事务升级为分布式事务:

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,如果我总结代码的操作顺序,我们会得到:

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

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


在save操作开始之前创建和打开连接的理由是确保在BeforeSave和AfterSave方法中完成的任何“额外”工作与save的其余部分属于同一事务。这是非常深思熟虑的。不过,也许我没抓住你的重点

如果使用默认构造函数创建一个新的
DbContext
,它将创建一个新的
DbConnection
。由于您同时拥有两个数据库连接,因此
TransactionScope
会将其升级为分布式事务

解决方案是使用相同的DbConnection创建第二个DbContext。
EFContextProvider
EntityConnection
属性允许:

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
}
保存实体之前的受保护覆盖字典(
字典(保存映射)
{
var secondContext=新的MyDbContext(EntityConnection);
//使用secondContext进行业务规则验证,
//例如,将新值与现有值进行比较
}
这样,就使用了相同的底层DbConnection,因此不会发生分布式事务

有一点关于DBContext是如何由Breeze创建的