当我们需要在我们的应用程序中进行数据库访问时,我们使用以下模式:
CreateOpenConnection
只执行new SqlConnection(myConnectionString)
并在其上调用Open()
。在我们执行查询之前调用此方法,并在查询返回后释放连接。work.Commit()
提交到数据库,如下所示:work.Commit:
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
内部不起作用,但我现在不认为这是一个问题)。但昨天我问了以下问题:
Serial processing of a certain message type in Rebus
答案似乎是使用Rebus的传奇功能。我尝试实现它并配置它,以便Rebus saga持久化到新的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();
}
我只是不确定这是否是正确的方法以及可能存在的缺点。
任何提示?
更新:为了澄清,问题不在于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;
}
}
当Rebus创建TransactionScope
时,以及Rebus打开SqlConnection
后,会调用此方法。打开我的 SqlConnection
后,它会尝试登记并崩溃
答案 0 :(得分:3)
我有点惊讶你看到了这一点,因为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
的命名表明你正在使用“短小精悍” - 在这种情况下,你可以将它传递给“短小精悍”,无论它是否为空,即
conn.Execute(sql, args, transaction: transaction);
答案 1 :(得分:1)
这在很大程度上取决于您使用的SQL Server版本。有关解决类似问题的其他问题,请参阅here。
它与SQL 2005和SQL 2008在处理同一TransactionScope
内的多个连接方面的不同有关。即,SQL 2008可以在同一TransactionScope
中打开多个连接,而不会升级到MSDTC。
这可能是你看到的问题
如果是这种情况,我认为只有两个选项是升级到SQL 2008或启用MSDTC。我知道这两个选项都可能是一个令人头疼的问题。