如果禁用MSDTC,如何绕过TransactionScope内的多个数据库连接?

我有一个Web应用程序,它向DAL中的3个数据库发出请求。 我正在编写一些集成测试,以确保整个function往返实际上完成了我期望它做的事情。 这与我的unit testing完全分开,只是fyi。

我打算写这些测试的方式是这样的

[Test] public void WorkflowExampleTest() { (using var transaction = new TransactionScope()) { Presenter.ProcessWorkflow(); } } 

在这种情况下,Presenter已经建立。 问题在ProcessWorkflow方法中发挥作用,因为它调用各种存储库,而这些存储库又访问不同的数据库,而我的sql server框没有启用MSDTC,所以每当我尝试创建新的sql连接时,我都会收到错误,或者尝试更改缓存连接的数据库以定位另一个数据库。

为简洁起见,Presenter类似于:

 public void ProcessWorkflow() { LogRepository.LogSomethingInLogDatabase(); var l_results = ProcessRepository.DoSomeWorkOnProcessDatabase(); ResultsRepository.IssueResultstoResultsDatabase(l_results); } 

我已经尝试了很多东西来解决这个问题。

  1. 始终缓存一个活动连接并更改目标数据库
  2. 为每个目标数据库缓存一个活动连接(这有点无用,因为池应该为我做这个,但我想看看我是否得到了不同的结果)
  3. 在每个存储库中添加其他TransactionScope,以便使用TransactionScopeOption“RequiresNew”拥有自己的事务

我在列表上的第三次尝试看起来像这样:

 public void LogSomethingInLogDatabase() { using (var transaction = new TransactionScope(TransactionScopeOption.RequiresNew)) { //do some database work transaction.Complete(); } } 

实际上我尝试的第三件事实际上是让unit testing工作,但所有完成的事务实际上都是我的数据库! 所以这完全是失败,因为整个观点是不影响我的数据库。

因此,我的问题是,鉴于我所制定的限制,还有哪些其他方案可以实现我想要做的事情?

编辑:

这就是“//做一些数据库工作”的样子

 using (var l_context = new DataContext(TargetDatabaseEnum.SomeDatabase)) { //use a SqlCommand here //use a SqlDataAdapter inside the SqlCommand //etc. } 

并且DataContext本身看起来像这样

 public class DataContext : IDisposable { static int References { get; set; } static SqlConnection Connection { get; set; } TargetDatabaseEnum OriginalDatabase { get; set; } public DataContext(TargetDatabaseEnum database) { if (Connection == null) Connection = new SqlConnection(); if (Connection.Database != DatabaseInfo.GetDatabaseName(database)) { OriginalDatabase = DatabaseInfo.GetDatabaseEnum(Connection.Database); Connection.ChangeDatabase( DatabaseInfo.GetDatabaseName(database)); } if (Connection.State == ConnectionState.Closed) { Connection.Open() //<- ERROR HAPPENS HERE } ConnectionReferences++; } public void Dispose() { if (Connection.State == ConnectionState.Open) { Connection.ChangeDatabase( DatabaseInfo.GetDatabaseName(OriginalDatabase)); } if (Connection != null && --ConnectionReferences <= 0) { if (Connection.State == ConnectionState.Open) Connection.Close(); Connection.Dispose(); } } } 

好的,我找到了解决这个问题的方法。 我这样做的唯一原因是因为我找不到任何其他方法来解决这个问题,并且因为它在我的集成测试中,所以我并不担心这会对生产代码产生负面影响。

我必须在我的DataContext中添加一个属性作为一个标志,以便在处理我的DataContext时跟踪是否处置连接对象。 这样,连接在整个事务范围内保持活动,因此不再困扰DTC

这是我新Dispose的样本:

 internal static bool SupressConnectionDispose { get; set; } public void Dispose() { if (Connection.State == ConnectionState.Open) { Connection.ChangeDatabase( DatabaseInfo.GetDatabaseName(OriginalDatabase)); } if (Connection != null && --ConnectionReferences <= 0 && !SuppressConnectionDispose) { if (Connection.State == ConnectionState.Open) Connection.Close(); Connection.Dispose(); } } 

这允许我的集成测试采取以下forms:

 [Test] public void WorkflowExampleTest() { (using var transaction = new TransactionScope()) { DataContext.SuppressConnectionDispose = true; Presenter.ProcessWorkflow(); } } 

我不建议在生产代码中使用它,但对于集成测试,我认为这是合适的。 另请注意,这仅适用于服务器始终相同的连接以及用户。

我希望这可以帮助遇到同样问题的其他人。

  1. 在连接字符串上设置Enlist = false以避免在事务上自动登记。

  2. 手动登记连接作为事务范围的参与者。 (http://msdn.microsoft.com/en-us/library/ms172153%28v=VS.80%29.aspx)

如果您不想使用MSDTC,则可以直接使用SQL事务。

请参见SqlConnection.BeginTransaction()