说我有一个连接到 n
数据库的Windows窗体应用程序,同时打开了 n
连接。
我正在寻找的是一次性与所有这些数据库进行交易。
例如,如果我有2个数据库连接:
using (ITransaction tx1 = session1.OpenTransaction())
{
using (ITransaction tx2 = session2.OpenTransaction())
{
// Do the query thingy here
}
}
首先编写所有这些都很好,但是当我想在这里和那里查询时,事情就变得多余了,更不用说添加新连接的可能性了。
我想要的是循环所有已注册的会话并将其包装在服务中,可能是这样的:
class TransactionManager
{
private ISession[] _sessions;
public TransactionManager(string[] connectionStrings)
{
// Initialize the sessions here
}
public function DoTransaction(string query)
{
foreach (ISession session in _sessions)
{
// What to do here? Using? Try-catch?
}
}
}
如果我在foreach
循环中使用,则意味着如果连接A成功但连接B不是,那么只会回滚连接B.
答案 0 :(得分:33)
看来你可能正在重新发明TransactionScope。在一个工作单元下完成所有这些工作非常简单*:
using (TransactionScope scope = new TransactionScope())
{
... Do Stuff with Connection 1 using SqlDataReader
... Do Stuff with Connection 2 using Entity Framework
... Do Stuff with Connection 3 on another Oracle Database
... And for good measure do some stuff in MSMQ or other DTC resource
scope.Complete(); // If you are happy
}
Stuff
根本不需要内联 - 它可以在不同的类中,也可以在不同的程序集中。没有必要使用TransactionScope
显式注册数据库或队列连接 - 一切都发生automagically
,前提是您使用的资源能够enlist into an ambient transaction。
现在是小字:
*
每当您同时使用多个数据库连接,或使用不同的连接字符串或多种技术时,这将需要2 phase commit并升级到DTC
交易以确保跨资源的ACID。 DTC本身有更多的小字体,并且提出了更多challenges in a corporate network,例如firewalls,clustering,security configuration和bugs。 但是,对于MS Sql Server上的轻量级事务,如果可以使用相同的数据库保持所有连接并且相同 连接字符串设置,并在打开前关闭每个连接 接下来,然后你can avoid DTC。
在提交或回滚事务之前,维护跨多个ACID资源的事务将始终保持对这些资源的锁定。这通常不会导致大批量企业的睦邻关系,因此请务必考虑锁定的后果。
如果Stuff
跨多个主题完成,则需要加入DependentTransaction
值得一提的最后一点是TransactionScope
的默认隔离级别是Serializable,它容易出现死锁。在大多数非关键情况下,您可能会将其降至Read Committed。
答案 1 :(得分:5)
使用TransactionScope,它将负责提交或回滚所有包含的交易:
using (var ts = new TransactionScope())
{
... // your old code
ts.Complete()
}