C# 异步使用同步WCF服务
我目前正在将客户机应用程序迁移到.NET4.5,以利用async/await。该应用程序是当前仅提供同步服务的WCF服务的客户端。我现在想知道,我应该如何异步使用这个同步服务 我正在使用连接到WCF服务,使用服务器和客户端之间共享的服务契约。因此,我无法使用VisualStudio或C# 异步使用同步WCF服务,c#,.net,wcf,async-await,C#,.net,Wcf,Async Await,我目前正在将客户机应用程序迁移到.NET4.5,以利用async/await。该应用程序是当前仅提供同步服务的WCF服务的客户端。我现在想知道,我应该如何异步使用这个同步服务 我正在使用连接到WCF服务,使用服务器和客户端之间共享的服务契约。因此,我无法使用VisualStudio或svcutil的自动生成来生成异步客户端代理 我已经阅读了关于是否使用Task.Run在客户端包装同步调用,或者是否使用异步方法扩展服务契约的内容。答案表明,使用服务器提供的“真正的”异步方法对客户机性能更好,因为没
svcutil的自动生成来生成异步客户端代理
我已经阅读了关于是否使用Task.Run
在客户端包装同步调用,或者是否使用异步方法扩展服务契约的内容。答案表明,使用服务器提供的“真正的”异步方法对客户机性能更好,因为没有线程需要主动等待服务调用完成。这对我来说很有意义,这意味着同步调用应该包装在服务器端
另一方面,斯蒂芬·图布(Stephen Toub)发现,一般情况下,他都是这样做的。现在,他没有提到WCF,所以我不确定这是否只适用于在同一台机器上运行的库,或者它是否也适用于远程运行的东西,但异步性的引入对连接/传输有实际影响
毕竟,由于服务器实际上并不是异步工作的(而且可能还要等一段时间),一些线程将始终必须等待:在客户端或服务器上。这也适用于同步使用服务时(当前,客户端在后台线程上等待以保持UI响应)
例子
为了使问题更清楚,我准备了一个例子。完整的项目可供下载
服务器提供同步服务GetTest
。这是目前存在的,并且是同步工作发生的地方。一种选择是将其包装在异步方法中,例如使用Task.Run
,并将该方法作为合同中的附加服务提供(需要扩展合同接口)
两者都可以正常工作,不会阻塞客户端。这两种方法都涉及到在某一端的忙等待:选项1在客户机上等待,这相当于以前在后台线程中所做的操作。选项2通过将同步方法包装在服务器上等待
让同步WCF服务异步感知的推荐方法是什么?我应该在哪里执行包装,在客户端还是在服务器上?或者,是否有更好的选项可以做到这一点而不必在任何地方等待,例如,通过在连接上引入“真正的”异步性,就像生成的代理一样?客户端和服务器端从异步的角度完全分开,它们根本不关心彼此。您应该在服务器上具有同步功能,而在服务器上仅具有同步功能
如果您希望做到“正确”,则在客户端上,您将无法重复使用用于生成通道工厂的接口与用于生成服务器的接口相同
所以您的服务器端看起来像这样
using System.ServiceModel;
using System.Threading;
namespace WcfService
{
[ServiceContract]
public interface IService
{
[OperationContract]
string GetTest();
}
public class Service1 : IService
{
public string GetTest()
{
Thread.Sleep(2000);
return "foo";
}
}
}
using System;
using System.Diagnostics;
using System.ServiceModel;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SandboxForm
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var button = new Button();
this.Controls.Add(button);
button.Click += button_Click;
}
private async void button_Click(object sender, EventArgs e)
{
var factory = new ChannelFactory<IService>("SandboxForm.IService"); //Configured in app.config
IService proxy = factory.CreateChannel();
string result = await proxy.GetTestAsync();
MessageBox.Show(result);
}
}
[ServiceContract]
public interface IService
{
[OperationContract(Action = "http://tempuri.org/IService/GetTest", ReplyAction = "http://tempuri.org/IService/GetTestResponse")]
Task<string> GetTestAsync();
}
}
你的客户会是这样的
using System.ServiceModel;
using System.Threading;
namespace WcfService
{
[ServiceContract]
public interface IService
{
[OperationContract]
string GetTest();
}
public class Service1 : IService
{
public string GetTest()
{
Thread.Sleep(2000);
return "foo";
}
}
}
using System;
using System.Diagnostics;
using System.ServiceModel;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SandboxForm
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var button = new Button();
this.Controls.Add(button);
button.Click += button_Click;
}
private async void button_Click(object sender, EventArgs e)
{
var factory = new ChannelFactory<IService>("SandboxForm.IService"); //Configured in app.config
IService proxy = factory.CreateChannel();
string result = await proxy.GetTestAsync();
MessageBox.Show(result);
}
}
[ServiceContract]
public interface IService
{
[OperationContract(Action = "http://tempuri.org/IService/GetTest", ReplyAction = "http://tempuri.org/IService/GetTestResponse")]
Task<string> GetTestAsync();
}
}
使用系统;
使用系统诊断;
使用System.ServiceModel;
使用System.Threading.Tasks;
使用System.Windows.Forms;
名称空间沙盒表单
{
公共部分类Form1:Form
{
公共表格1()
{
初始化组件();
var按钮=新按钮();
此.Controls.Add(按钮);
按钮。单击+=按钮\u单击;
}
私有异步无效按钮\u单击(对象发送方,事件参数e)
{
var factory=newchannelfactory(“SandboxForm.IService”);//在app.config中配置
IService proxy=factory.CreateChannel();
string result=await proxy.getestasync();
MessageBox.Show(结果);
}
}
[服务合同]
公共接口设备
{
[运营合同(行动=”http://tempuri.org/IService/GetTest,ReplyAction=http://tempuri.org/IService/GetTestResponse")]
任务getestasync();
}
}
如果您的服务器端API可以自然异步(比如您的任务.Delay
示例,而不是任务.Run
包装器),则在合同界面中将其声明为基于任务的。否则,只需保持同步(但不要使用Task.Run
)。不要为同一方法的同步和异步版本创建多个端点
对于异步和同步合约API,生成的WSDL保持不变,我刚刚发现自己:。您的客户端将保持不变运行。通过使服务器端WCF方法异步,您所做的只是提高服务的可伸缩性。当然,这是一件很好的事情,但是将同步方法包装为Task.Run
会损害可伸缩性,而不是改进它
现在,WCF服务的客户机不知道该方法是在服务器上实现为同步还是异步,也不需要知道这一点。客户机可以同步调用您的方法(并阻止客户机的线程),也可以异步调用它(而不阻止客户机的线程)。在这两种情况下,它都不会改变这样一个事实,即当方法在服务器上完全完成时,SOAP响应消息将只发送到客户机
在中,您试图以不同的合同名称公开同一API的不同版本:
[ServiceContract]
public interface IExampleService
{
[OperationContract(Name = "GetTest")]
string GetTest();
[OperationContract(Name = "GetTestAsync")]
Task<string> GetTestAsync();
[OperationContract(Name = "GetTestRealAsync")]
Task<string> GetTestRealAsync();
}
[服务合同]
公共接口IExampleService
{
[运营合同(Name=“GetTest”)]
字符串GetTest();
[运营合同(Name=“getestasync”)]
任务getestasync();
[OperationContract(Name=“GetTestRealAsync”)]
任务获取
[ServiceContract]
public interface IExampleService
{
[OperationContract(Name = "GetTest")]
string GetTest();
[OperationContract(Name = "GetTestAsync")]
Task<string> GetTestAsync();
[OperationContract(Name = "GetTestRealAsync")]
Task<string> GetTestRealAsync();
}
[ServiceContract]
public interface IExampleService
{
[OperationContract]
Task<string> GetTestAsync(bool runSynchronously);
}
Task<string> GetTestAsync(bool runSynchronously)
{
if (runSynchronously)
return GetTest(); // or return GetTestAsyncImpl().Result;
else
return await GetTestAsyncImpl();
}