通过查看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);
}
答案 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方法中完成的任何“额外”工作都是与保存的其余部分相同的事务的一部分。这是非常慎重的。但是,也许我错过了你的观点。