我有一个WPF应用程序,我正在使用WCF分离到客户端和服务器端。我不喜欢我最初用直接解决方案得到的混乱,所以现在我按照Miguel Castro,WCF Extreme的截屏视频中的建议进行重组。如果您不熟悉该视频,他基本上手动设置整个通信 - 不使用服务引用。这包括:
我已经完成了他所有的步骤,我真的很喜欢这里的发展方向。但是,他没有解决异步服务调用,这就是我想要使用的。
添加服务引用时,我可以选中“生成异步操作”复选框,然后获取MyServiceCompleted和MyServiceAsync。但是,我想这是在添加服务引用时生成的内容,而不是在构建的类中有一些魔力?
那么,我能以某种方式从ClientBase或ClientFactory获取异步操作吗?或者我是否必须将实际的服务器端服务定义为异步?如果是这样 - 有人可以给我一些关于如何开始使用简单异步服务的提示或示例吗?我一直在MSDN上阅读这个主题,但它让我感到很困惑,因为我已经没有得到这个了...
答案 0 :(得分:5)
在服务器端实现异步操作非常简单。确保方法名称匹配,并以Begin和End为前缀。 GetImageAsyncResult是一个自定义的IAsyncResult实现(很多Web上的例子)。
public class MapProvider : IMapProvider //implementation - belongs to server
{
public IAsyncResult BeginGetImage(int level, int x, int y, string[] layers, AsyncCallback callback, object state)
{
GetImageAsyncResult asyncResult = new GetImageAsyncResult(level, x, y, layers, callback, state);
ThreadPool.QueueUserWorkItem(Callback, asyncResult);
return asyncResult;
}
private void Callback(object state)
{
GetImageAsyncResult asyncResult = state as GetImageAsyncResult;
asyncResult.Image = TileProvider.GetImage(asyncResult.Level, asyncResult.X, asyncResult.Y, asyncResult.Layers);
asyncResult.Complete();
}
public System.Drawing.Bitmap EndGetImage(IAsyncResult result)
{
using (GetImageAsyncResult asyncResult = result as GetImageAsyncResult)
{
asyncResult.AsyncWait.WaitOne();
return asyncResult.Image;
}
}
}
public class MapProviderProxy : ClientBase<IMapProvider>, IMapProvider, IDisposable
{
public IAsyncResult BeginGetImage(int level, int x, int y, string[] layers, AsyncCallback callback, object state)
{
return Channel.BeginGetImage(level, x, y, layers, callback, state);
}
public System.Drawing.Bitmap EndGetImage(IAsyncResult result)
{
return Channel.EndGetImage(result);
}
public void Dispose()
{
if (State == CommunicationState.Faulted)
{
Abort();
}
else
{
try
{
Close();
}
catch
{
Abort();
}
}
}
}
答案 1 :(得分:3)
当你选择“生成异步操作”时,如你所说,没有任何魔法:Begin...
将开始你的通信,服务器开始处理东西,但你不能使用任何东西,直到你调用{{1 }}
所有这些行为都发生在客户端,因此您无需在服务实现中更改任何内容。
你可能认为这必须很复杂,但事实并非如此;)
编辑:举个例子:
End...
您还可以使用using (Service.SampleClient client = new Service.SampleClient())
{
client.AddCompleted +=
(object sender, Service.AddCompletedEventArgs e)
{
Console.WriteLine(e.Result); // 2
};
client.AddAsync(1, 1);
// wait for async callback
Console.ReadLine();
}
[ServiceContract]
public interface ISample
{
[OperationContract]
int Add(int a, int b);
}
答案 2 :(得分:3)
在不使用svcutil的情况下在客户端实现异步操作的另一种方法是在客户端本地设置一个实现异步操作的接口(ServiceContract)。
服务器端的合同:
[ServiceContract]
public interface IServerContract
{
[OperationContract]
string GetData(int value);
}
客户端的异步合同
[ServiceContract(Namespace = "http://tempuri.org/", Name = "IServerContract")]
public interface IClientContractAsync
{
[OperationContract]
Task<string> GetDataAsync(int value);
}
请注意,需要在客户端契约上设置名称和默认命名空间,以匹配服务器契约的命名空间。所以现在你有一个异步操作(希望没有启动任何新线程)。这样您就不必在服务器端执行任何特定实现。当然,这类似于SvcUtil所做的,但SvcUtil会生成大量额外的代码,有时我发现svcutil会导致问题,例如:重用类。
ClientProxy
public class ClientProxy : ClientBase<IClientContractAsync>
{
public IClientContractAsync ChannelOut
{
get
{
return Channel;
}
}
}
客户端的用法:
static void Main(string[] args)
{
var client = new ClientProxy();
client.Open();
var asyncReadValue = client.ChannelOut.GetDataAsync(45).Result;
Console.WriteLine(asyncReadValue);
Console.ReadLine();
}
服务器类
public class ServerService : IServerContract
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
}
服务器主机
static void Main(string[] args)
{
var server = new ServiceHost(typeof(ServerService));
server.Open();
Console.WriteLine("started");
Console.ReadLine();
}