【发布时间】:2013-05-31 09:50:02
【问题描述】:
当我们需要在应用程序中进行数据库访问时,我们使用以下模式:
- 对于查询,我们有一个静态工厂类,其方法为
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 中使用我们的旧实体框架查询和手动 SqlConnections 不起作用,但我不'现在不认为这是一个问题)。但是昨天我问了以下问题:
Serial processing of a certain message type in Rebus
答案似乎是使用 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(),我很确定它会起作用,但我从来没有到达那里,因为我的查询 em> 失败了。
失败的例子:
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 之后,都会调用它。在打开 my SqlConnection 时,它会尝试注册并崩溃
【问题讨论】:
-
如果您使用“Enlist=false”,那肯定会让您的
TransactionScope毫无意义?因为连接不会在其中 -
同样,您的
BeginTransaction代码也不使用事务 - 需要在命令中明确指定 ADO.NET 事务,因此您需要将transaction传递给CommitChanges,确定吗? -
您能否明确说明您使用的是哪个版本的sql server?
标签: c# sql-server-2012 transactionscope msdtc rebus