在事务中调用 WCF 方法会导致错误

Calling WCF method inside a Transaction cause error

提问人:Masoud 提问时间:11/1/2023 最后编辑:Masoud 更新时间:11/14/2023 访问量:72

问:

我有一个复杂的应用程序,遇到了一个错误。我用下面的代码简化了我的实际问题。

我有一个 WCF 服务,其中包含对我的一个表执行 select 的方法:

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:等待操作超时

问题出在哪里?

C# WCF 实体框架 6 事务范围

评论

0赞 Panagiotis Kanavos 11/1/2023
为什么首先使用 TransactionScope?DbContext 已经是一个工作单元。它不需要外部交易。调用时,所有跟踪的更改都保存在单个内部数据库事务中,如果释放了 DbContext,则丢弃所有跟踪的更改。删除并仅在要完成所有更改时调用SaveChangesTransactionScopeSaveChanges
0赞 Panagiotis Kanavos 11/1/2023
WCF 支持 WS-Transactions 标准,因此在使用 TransactionScope 时,会自动将 WCF 调用和 EF Core 的内部事务注册到同一个分布式事务中。如果 WCF 连接花费的时间过长,则整个事务可能会超时。如果在同一范围内打开多个连接,则所有连接都将成为同一分布式事务的一部分,并且很容易相互阻塞。如果在作用域内使用多个 DbContext,而不是使用单个 DbContext,则会面临相同的风险,因为内部连接将被卷入事务
0赞 Panagiotis Kanavos 11/1/2023
代码的其余部分是什么样子的,为什么使用显式事务?几乎可以肯定的是,重写代码以避免阻塞和超时是可能的
0赞 Masoud 11/1/2023
@PanagiotisKanavos:我用这个问题简化了我的实际问题,我之所以使用,是因为我需要在调用WCF服务后更新数据。我使用 EF 而不是 EF Core 的另一件事。TransactionScope
0赞 Panagiotis Kanavos 11/1/2023
然后拨打电话后。你使用什么ORM并不重要。DbContext/Session(在 NHibernate 中)是一个工作单元,它将所有挂起的更改保存在内存中,并一次性存储所有更改。SaveChanges

答:

0赞 Steve Py 11/14/2023 #1

问题在于,您正在尝试在事务范围内释放 DbContext。Tx 作用域将希望确保同时提交对 DbContext 的更改以及识别它的任何其他更改,这将阻止 .在它的块的末尾,这很可能是异常的来源。DbContextDisposeusing

如果 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,但随后仍然希望选择“退出”该插入。在极少数情况下,这可能是一个合理的问题,但通常情况下,它只是一个有限的实现,有更好的选择可以考虑。