我有一个复杂的应用程序并遇到了错误。我用以下代码简化了我的实际问题。
我有一个 WCF 服务,其方法可以在我的一个表上执行选择:
public string FindDocumentNumber(int documentId)
{
using(var context = new MyDbContext())
{
return context.Documents.Where(x=>x.Id == documentId && x.Tag == "*")
.Select(x=>x.Number).FirstOrDefault();
}
}
在我的控制台应用程序中,我有以下代码:
using(var scope = new TransactionScope())
{
using(var context = new MyDbContext())
{
var doc = context.Documents.Where(x=>x.Id == 12345).FirstOrDefault();
doc.Tag = "*";
context.SaveChange();
}
...
var number = myWCFService.FindDocumentNumber(12345); // At this point, I encounter an error
...
//some other updates on document with Id = 12345
scope.Complete();
}
但是,运行代码时,遇到以下错误:
System.Data.Entity.Core.EntityCommandExecutionException
HResult=0x8013193C Message=执行时发生错误 命令定义。有关详细信息,请参阅内部异常。
来源= StackTrace:内部异常1:SqlException:执行超时已过期。这 操作完成之前超时时间已过或 服务器没有响应。
内部异常2:Win32Exception:等待操作超时
问题出在哪里?
问题在于您尝试在事务范围内处置 DbContext。 Tx 范围将希望确保对 DbContext 的更改以及识别它的任何其他内容都会同时提交,这将阻塞
DbContext
。Dispose
在其 using
块的末尾,这是最重要的。可能是您异常的根源。
如果 API 调用设置为在 Tx 范围内工作(我怀疑不是),那么您需要反转
using
块以避免该错误:
using(var context = new MyDbContext())
{
using(var scope = new TransactionScope())
{
var doc = context.Documents.Where(x=>x.Id == 12345).Single();
doc.Tag = "*";
context.SaveChange();
...
var number = myWCFService.FindDocumentNumber(12345); // At this point, I encounter an error
...
//some other updates on document with Id = 12345
scope.Complete();
}
}
然而,正如评论所说,Tx Scope 可能甚至没有必要。 DbContext 包装了自己的事务,因此如果存在您不想将更改提交到数据库的情况,只需避免调用“SaveChanges”,直到您满意所有内容都应该提交为止。
using(var context = new MyDbContext())
{
var doc = context.Documents.Where(x=>x.Id == 12345).Single();
doc.Tag = "*";
...
var number = myWCFService.FindDocumentNumber(12345); // At this point, I encounter an error
...
//some other updates on document with Id = 12345
if (everythingIsOk) // Use a flag, check state, whatever....
context.SaveChanges();
}
事务范围旨在协调多个单独的操作,以确保它们全部一起提交或回滚。我看到使用单独事务的一个常见场景是,开发人员觉得他们需要先保存记录,例如获取生成的 ID,以便他们可以在其他地方使用或设置该 ID,但仍然希望选择“退出”该插入物。在极少数情况下,这是一个合法的问题,但更多时候这只是有限的实现,需要考虑更好的选择。