当我们需要在应用程序中进行数据库访问时,我们使用以下模式:
- 对于查询,我们有一个静态工厂类,它的方法
CreateOpenConnection
无非就是new SqlConnection(myConnectionString)
调用Open()
它。在我们进行查询之前调用此方法,并在查询返回后释放连接。 - 对于插入/更新/删除,我们使用工作单元模式,其中更改被批量化并通过如下调用提交到数据库
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
,以便在处理消息失败的情况下,可以回滚内容。现在,这本身到目前为止运行良好。我可以SqlConnection
在 Rebus 消息处理程序中打开一个新的,没有任何问题(但是,在同一个 Rebus 中使用我们的旧实体框架查询和手动 SqlConnectionsTransactionScope
不起作用,但我现在不认为这是一个问题)。但是昨天我问了以下问题:
答案似乎是使用 Rebus 的 saga 功能。我尝试实现它并对其进行配置,以便将 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();
}
我只是不确定这是否是正确的方法以及可能的缺点是什么。
有小费吗?
更新:澄清一下,这不是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;
}
}
当 aTransactionScope
被 Rebus 创建时,以及 aSqlConnection
被 Rebus 打开后,它会被调用。打开my SqlConnection
后,它会尝试注册并崩溃