当我们需要在应用程序中进行数据库访问时,我们使用以下模式:
- 对于查询,我们有一个静态工厂类,其中包含一个名为
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
而没有任何问题(但是,在同一个Rebus TransactionScope
中同时使用我们的旧版Entity Framework查询和手动SqlConnection就不行,但我现在不认为这是问题)。但是昨天,我问了以下问题:
答案似乎是使用Rebus的saga功能。我尝试实现这个并将其配置为使Rebus saga持续到一个新的SQL Server数据库(具有不同的连接字符串)。可能因为使用了该SQL Server持久性,而导致创建了一个SqlConnection
,因为现在每次我尝试创建SqlConnection
时,都会出现以下异常:
分布式事务管理器(MSDTC)的网络访问已被禁用。请使用Component Services管理工具为MSDTC在安全配置中启用网络访问的DTC。
为配置和性能开销,我非常非常希望避免启用MSDTC。虽然我可能错了,但它也似乎无必要。
我猜这里发生的事情是Rebus创建了一个环境的TransactionScope
,并且它所创建的SqlConnection
注册到了该scope。当我尝试创建自己的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
后以及打开SqlConnection
后被调用。在打开我的SqlConnection
时,它尝试注册并崩溃。
TransactionScope
无意义,因为连接不会在其中。 - Marc GravellBeginTransaction
代码也没有使用事务 - ADO.NET 事务需要在命令中显式指定,所以你肯定需要将transaction
传递到CommitChanges
中,对吧? - Marc Gravell