C#控制跨多个数据库的事务

假设我有一个连接到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循环中使用use,则意味着如果连接A成功但连接B不成功,那么只有连接B将被回滚。

看来你可能正在重新发明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发生。

现在小字:

  • *任何时候同时使用多个数据库连接,或不同的连接字符串或多种技术,这将需要2阶段提交并升级到DTC事务,以确保跨资源的ACID。 DTC本身具有更多小字体,并且在企业网络中提出了更多挑战 ,例如防火墙 , 集群 , 安全配置和错误 。
  • 但是,对于MS Sql Server上的轻量级事务,如果可以使用相同的数据库和相同的连接字符串设置保持所有连接,并在打开下一个连接之前关闭每个连接,则可以避免DTC 。

  • 在提交或回滚事务之前,维护跨多个ACID资源的事务将始终保持对这些资源的锁定。 这通常不会在大批量企业中实现良好的邻居关系,因此请务必考虑锁定的后果。

  • 如果Stuff是跨多个线程完成的,那么你需要依赖DependentTransaction

  • 值得一提的最后一点是TransactionScope的默认隔离级别是Serializable,它容易出现死锁。 在大多数非关键场景中,您可能会将其降低到Read Committed 。

使用TransactionScope,它将负责提交或回滚所有包含的事务:

 using (var ts = new TransactionScope()) { ... // your old code ts.Complete() }