我有一种方法,我希望在抽象意义上是"事务性的".它调用了两个碰巧对数据库做事的方法,但是这个方法并不知道.
public void DoOperation()
{
using (var tx = new TransactionScope())
{
Method1();
Method2();
tc.Complete();
}
}
public void Method1()
{
using (var connection = new DbConnectionScope())
{
// Write some data here
}
}
public void Method2()
{
using (var connection = new DbConnectionScope())
{
// Update some data here
}
}
Run Code Online (Sandbox Code Playgroud)
因为实际上TransactionScope意味着将使用数据库事务,所以如果我们从池中获得两个不同的连接,我们就会遇到一个问题,即它可以被提升为分布式事务.
我可以通过DoOperation()在ConnectionScope中包装方法来解决这个问题:
public void DoOperation()
{
using (var tx = new TransactionScope())
using (var connection = new DbConnectionScope())
{
Method1();
Method2();
tc.Complete();
}
}
Run Code Online (Sandbox Code Playgroud)
我DbConnectionScope为了这个目的而做了自己,所以我不必将连接对象传递给子方法(这是比我真正的问题更为人为的例子).我从这篇文章中得到了这个想法:http://msdn.microsoft.com/en-us/magazine/cc300805.aspx
但是我不喜欢这种解决方法,因为它意味着DoOperation现在知道它调用的方法可能使用连接(并且可能每个都使用不同的连接).我怎么能重构这个来解决这个问题呢?
我正在考虑的一个想法是创建一个更通用的OperationScope,所以当与我将编写的自定义Castle Windsor生活方式合作时,将意味着容器所请求的任何组件OperationScopeLifetyle将始终获得该组件的相同实例.这确实解决了问题,因为OperationScope它比模糊不清楚 DbConnectionScope.
我在这里看到相互矛盾的要求。
一方面,您不想知道DoOperation数据库连接正用于其子操作这一事实。
另一方面,它显然意识到了这一事实,因为它使用了TransactionScope.
当你说你希望它是抽象意义上的事务性时,我可以理解你的意思,但我对此的看法是,实际上不可能(不,从头开始 -完全不可能)以这样的方式描述事务抽象术语。假设您有一堂这样的课程:
class ConvolutedBusinessLogic
{
public void Splork(MyWidget widget)
{
if (widget.Validate())
{
widgetRepository.Save(widget);
widget.LastSaved = DateTime.Now;
OnSaved(new WidgetSavedEventArgs(widget));
}
else
{
Log.Error("Could not save MyWidget due to a validation error.");
SendEmailAlert(new WidgetValidationAlert(widget));
}
}
}
Run Code Online (Sandbox Code Playgroud)
这个类至少做了两件可能无法回滚的事情(设置类的属性并执行事件处理程序,例如,这可能会级联更新表单上的某些控件),并且至少还有另外两件事情绝对不能回滚(附加到某处的日志文件并发送电子邮件警报)。
也许这看起来像是一个人为的例子,但这实际上是我的观点;你不能将 a 视为TransactionScope“黑匣子”。范围实际上是一个像其他任何依赖项一样的依赖项;TransactionScope只是为工作单元提供了一个方便的抽象,但它可能并不总是合适的,因为它实际上并没有包装数据库连接并且无法预测未来。特别是,当单个逻辑操作需要跨越多个数据库连接时,无论这些连接是连接到同一个数据库还是不同的数据库,它通常是不合适的。当然,它会尝试处理这种情况,但正如您已经了解到的,结果并不是最优的。
在我看来,你有几个不同的选择:
Method1通过让Method2它们采用连接参数,或者将它们重构为采用连接依赖项(构造函数或属性)的类,明确表明 和 需要连接。这样,连接就成为契约的一部分,因此Method1不再知道太多——它确切地知道根据设计它应该知道的内容。
接受你的DoOperation方法确实知道什么Method1和Method2做什么。其实这并没有什么错! 确实,您不想依赖某些未来调用的实现细节,但抽象中的前向依赖通常被认为是可以的;您需要关心的是反向依赖关系,就像当域模型深处的某个类尝试更新它一开始就没有业务知道的 UI 控件时。
使用更强大的工作单元模式(也:此处)。这变得越来越流行,总的来说,这是 Microsoft 在 Linq to SQL 和 EF(/DataContext基本上ObjectContext是 UOW 实现)方面所采用的方向。这与 DI 框架很好地结合在一起,本质上使您无需担心事务何时开始和结束以及数据访问如何发生(术语是“持久性无知”)。这可能需要对您的设计进行大量修改,但从长远来看,这将是最容易维护的。
希望其中之一对您有所帮助。
| 归档时间: |
|
| 查看次数: |
504 次 |
| 最近记录: |