使用 WCF 中的任务构建异步操作合同的正确方法

Posted

技术标签:

【中文标题】使用 WCF 中的任务构建异步操作合同的正确方法【英文标题】:Correct way to build asynchronously operation contract using a Task in WCF 【发布时间】:2017-10-16 07:46:50 【问题描述】:

我正在构建一个 WCF 服务,它接受来自 HTTP 客户端的大块数据(通常为 4MB)POST,操作契约是一个具有 Stream 类型参数的函数,如下所示:

[OperationContract(IsOneWay = true)]
[WebInvoke(Method = "POST", UriTemplate = "UploadData", BodyStyle = WebMessageBodyStyle.Bare)]
void UploadData(Stream stream);

由于 UploadData 将由 I/O 完成线程运行,并且我知道数据的处理可能需要一段时间,所以一般我应该尽快将其返回到 I/O 完成线程池,否则会影响并发性,所以我计划启动一个任务来将流复制到 MemoryStream 并从那里进行以下数据处理。 让我困惑的是,由于WCF创建并维护了流对象,Task.Run返回后,UploadData退出,WCF认为这个请求已经被服务,但实际上我只是开始将流复制到一个MemoryStream中,如何确保流对象仍然存在并且在复制完成之前没有被 WCF 释放?

public void UploadData(Stream stream)

    Stream incomingStream = stream; // is variable capture necessary here?
    Task.Run(() =>
    
        using (MemoryStream memoryStream = new MemoryStream())
        
            using (incomingStream)
            
                stream.CopyTo(memoryStream);
            

            memoryStream.Seek(0, SeekOrigin.Begin);

            // process data
        
    

当然,我可以在 Task 开始之前进行流复制,但这看起来并不干净,实际上并不能回答我的困惑。

我的第二个问题是,如果我在操作合同中使用任务,我应该保持操作合同的签名同步还是异步?如果我将运营合同更改为:

[OperationContract(IsOneWay = true)]
[WebInvoke(Method = "POST", UriTemplate = "UploadData", BodyStyle = WebMessageBodyStyle.Bare)]
Task UploadDataAsync(Stream stream);

【问题讨论】:

【参考方案1】:

起初:你不需要Stream incomingStream = stream;

我有相同的服务,起初我像你一样实施它。我向您保证,在复制或完成任何类型的过程之前,WCF 不会处理流。但还有另一个问题。你可能会得到线程外异常。(你可以阅读更多关于Task.Runhere的信息。)。所以我让我的服务同步,但我称之为异步。这样我就可以达到最大的性能。

【讨论】:

以上是关于使用 WCF 中的任务构建异步操作合同的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

可移植/可互操作的 WCF 合同

为外部服务的操作合同控制 WCF 代码生成的方法

WCF - 使用完全相同的数据合同的多个服务合同

WCF 仅异步操作

在 PCL 服务参考设置中禁用基于任务的异步操作

在反序列化时使用动态和强制执行 WCF 合同