当我们需要在应用程序中访问数据库时,我们使用以下模式:
- 对于查询,我们有一个静态工厂类,其方法
- 对于插入/更新/删除,我们使用工作单元模式,其中更改被批处理并通过调用
work.Commit()
提交给数据库,如下所示:
CreateOpenConnection
只做new SqlConnection(myConnectionString)
,并在其上调用Open()
。在执行查询之前调用该方法,并在查询返回后处理连接。工作。提交:
using (var tranScope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
using (var conn = DapperFactory.CreateOpenConnection())
{
var count = _changeTracker.CommitChanges(conn);
tranScope.Complete();
return count;
}
}
这似乎很适合作为web服务的一部分,但是当我试图将其与Rebus结合使用时,目前给我的MSDTC带来了麻烦。
据我所知,Rebus(当它处理队列中的消息时)创建了一个新的TransactionScope
,以便在处理消息失败的情况下,可以回滚内容。到目前为止,这本身运行得很好。我可以在Rebus消息处理程序中打开一个新的SqlConnection
而没有任何问题(然而,使用我们的传统实体框架查询和手动SqlConnections在相同的Rebus TransactionScope
中不起作用,但我现在不认为这是一个问题)。但是昨天我问了以下问题:
Rebus中某一消息类型的串行处理
这个问题的答案似乎是使用Rebus的传奇特征。我尝试实现这一点,并对其进行了配置,以便将Rebus传奇持久化到新的SQL Server数据库(具有不同的连接字符串)。大概,使用SQL Server持久性打开自己的SqlConnection
,因为任何时候我尝试创建SqlConnection
现在,我得到以下异常:
分布式事务管理器(MSDTC)的网络访问已被禁用。请使用组件服务管理工具在MSDTC的安全配置中启用DTC进行网络访问。
启用MSDTC是我非常,非常非常喜欢避免做的事情,关于配置和性能开销。也许我错了,但这似乎也没有必要。
我假设这里发生的是Rebus创建了一个环境TransactionScope
,并且它创建的SqlConnection
加入了该作用域。当我尝试创建自己的SqlConnection
时,它也试图加入该环境作用域,因为涉及多个连接,它被提升为MSDTC,这失败了。
我有一个关于如何解决这个问题的想法,但我不知道这样做是否正确。我要做的是:
- 将
Enlist=false
添加到我的应用程序的连接字符串,以便它永远不会加入环境事务。 - 修改
Commit
方法,使其不创建新的TransactionScope
(我的连接不再订阅,因为我刚刚告诉它不应该),但它使用conn.BeginTransaction
。
一样:
var transaction = conn.BeginTransaction();
try
{
var count = _changeTracker.CommitChanges(conn);
transaction.Commit();
return count;
}
catch
{
transaction.Rollback();
throw;
}
finally
{
transaction.Dispose();
}
我只是不确定这是否是正确的方法,以及可能的缺点是什么。
提示吗?
UPDATE:澄清一下,这不是work.Commit()
一直给我的问题,我很确定它会起作用,但我从来没有到那里,因为我的查询是什么失败了。
失败的例子:
public int? GetWarehouseID(int appID)
{
var query = @"
select top 1 ID from OrganizationUnits o
where TypeID & 16 = 16 /* warehouse */";
using (var conn = _dapper.OpenConnection())
{
var id = conn.Query<int?>(query).FirstOrDefault();
return id;
}
}
当一个TransactionScope
被Rebus创建时,以及一个SqlConnection
被Rebus打开后,这个函数被调用。在打开my SqlConnection
时,它试图登记并崩溃
我有点惊讶你看到这个,因为RequiresNew
应该意味着它是与其他事务隔离的;通常,此消息意味着在事务范围内激活了2个连接-您是否确定在该块内没有其他代码创建/打开连接?
你提出的解决方案应该工作-虽然在某些方面TransactionScopeOption.Suppress
可能比改变你的配置更方便(但两者都应该工作)。然而,这里有一个问题:ADO。. NET事务必须传递给各个命令,因此您需要(还需要整理一下代码):
using(var transaction = conn.BeginTransaction()) {
try {
var count = _changeTracker.CommitChanges(conn, transaction);
transaction.Commit();
return count;
} catch {
transaction.Rollback();
throw;
}
}
CommitChanges
接受一个事务——可能使用可选参数:
int CommitChanges(DbConnection connection, DbTransaction transaction = null)
{ ... }
您对DapperFactory
的命名表明您正在使用"dapper"-在这种情况下,您可以将其传递给"dapper",无论它是否为空,即
conn.Execute(sql, args, transaction: transaction);
这很大程度上取决于你使用的SQL Server的版本。请参阅这里的另一个SO问题,解决类似的问题。
这与SQL 2005和SQL 2008在处理同一TransactionScope
内的多个连接时的不同有关。例如,SQL 2008可以在同一个TransactionScope
中打开多个连接,而无需升级到MSDTC。
这是您看到的问题吗
如果是这种情况,我认为只有两个选择是升级到SQL 2008或启用MSDTC。我知道这两个选项可能都很令人头疼。