WCF 的异步调用是基于消息交换(Message Exchange)来实现的,和我们通常使用委托来实现异步调用有所不同。
编写步骤:
1. 创建服务契约。
public interface ICalculate
{
[OperationContract]
int Add(int a, int b);
}
2. 为契约方法添加异步版本。
我们为 Add 方法添加了 BeginAdd 和 EndAdd 两个在 .NET SDK 中 "常见" 的异步操作方法。注意 BeginAdd 方法中我们添加了异步声明,而 EndAdd 方法没有。还有就是要注意异步版本方法的参数。
public interface ICalculate
{
[OperationContract]
int Add(int a, int b);
[OperationContract(AsyncPattern=true)]
IAsyncResult BeginAdd(int a, int b, AsyncCallback callBack, object state);
int EndAdd(IAsyncResult ar);
}
3. 实现服务契约。
你可能注意到了,我们并没有创建 Add 的委托原型,也没有 "真正" 实现 BeginAdd 和 EndAdd。这是因为消息交换会 "异步" 调用 Add 方法,所有的异步版本方法只是用来创建消息声明而已。
{
public int Add(int a, int b)
{
Thread.Sleep(5000);
Console.WriteLine(OperationContext.Current.SessionId);
return a + b;
}
public IAsyncResult BeginAdd(int a, int b, AsyncCallback callBack, object state)
{
throw new Exception("The method or operation is not implemented.");
}
public int EndAdd(IAsyncResult ar)
{
throw new Exception("The method or operation is not implemented.");
}
}
4. 我们给一个完整版本,看看执行结果。
public interface ICalculate
{
[OperationContract]
int Add(int a, int b);
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginAdd(int a, int b, AsyncCallback callBack, object state);
int EndAdd(IAsyncResult ar);
}
public class CalculateService : ICalculate
{
public int Add(int a, int b)
{
Console.WriteLine("服务器方法 Add 开始执行: {0}", DateTime.Now);
try
{
Thread.Sleep(5000);
return a + b;
}
finally
{
Console.WriteLine("服务器方法 Add 执行完成: {0}", DateTime.Now);
}
}
public IAsyncResult BeginAdd(int a, int b, AsyncCallback callBack, object state)
{
throw new Exception("The method or operation is not implemented.");
}
public int EndAdd(IAsyncResult ar)
{
throw new Exception("The method or operation is not implemented.");
}
}
public class WcfTest
{
public static void Test()
{
AppDomain.CreateDomain("Server").DoCallBack(delegate
{
ServiceHost host = new ServiceHost(typeof(CalculateService));
host.AddServiceEndpoint(typeof(ICalculate), new WSHttpBinding(),
"http://localhost:8080/calc");
host.Open();
});
ICalculate channel = ChannelFactory<ICalculate>.CreateChannel(new WSHttpBinding(),
new EndpointAddress("http://localhost:8080/calc"));
using (channel as IDisposable)
{
Console.WriteLine("客户端调用 BeginAdd: {0}", DateTime.Now);
IAsyncResult ar = channel.BeginAdd(1, 2, delegate { Console.WriteLine("CallBack..."); }, null);
Console.WriteLine("客户端调用 BeginAdd 完成: {0}", DateTime.Now);
Console.WriteLine(channel.EndAdd(ar));
Console.WriteLine("客户端调用 EndAdd 完成: {0}", DateTime.Now);
}
}
}
输出:
客户端调用 BeginAdd: 2007-4-1 20:56:47
客户端调用 BeginAdd 完成: 2007-4-1 20:56:47
服务器方法 Add 开始执行: 2007-4-1 20:56:49
服务器方法 Add 执行完成: 2007-4-1 20:56:54
3
客户端调用 EndAdd 完成: 2007-4-1 20:56:55
CallBack...
最后需要注意的是,我们必须使用支持 Session 的 Binding 对象 (BasicHttpBinding 会抛出异常)。
附:其实最简单的办法不是手工添加 AsyncPattern / BeginXXX / EndXXX,而是手工使用 svcutil.exe 生成客户端代理,记住加上 "/async" 参数。