从WCF中的异步回调返回值

时间:2013-02-01 10:51:31

标签: c# wcf mvvm asynchronous

具有MVVM模式的我的WPF应用程序基本上应执行以下操作:

  • 按钮视图绑定到视图模型中的命令。 - >检查!
  • 视图模型中的命令异步查询Web服务以获取CProject个对象的列表,以便将其放入ProjectList属性。 - >检查!

这看起来像这样......

视图模型中的命令:

proxy = new SomeService();
proxy.GetProjectList(GetProjectListCallback, username, password);

在视图模型中回调:

private void GetProjectListCallback(object sender, GetProjectListCompletedEventArgs e) {
  this.ProjectList = e.Result;
}

SomeService实现了一个接口ISomeService

public void GetProjectList(EventHandler<GetProjectListCompletedEventArgs> callback, string username, string password) {
  service.GetProjectListCompleted += callback;
  service.GetProjectListAsync(username, password);
}

到目前为止这个工作正常。但是我觉得我想将这个回调移动到服务本身,以便视图模型只调用类似的东西:

proxy = new SomeService();
this.ProjectList = proxy.GetProjectList(username, password);

但是当将回调移动到服务时,它如何将e.Result返回到调用视图模型?或者使用Task更好的主意?

2 个答案:

答案 0 :(得分:1)

不幸的是,你不能从这样的异步操作返回一个值 - 你必须阻止该线程等待它完成,这相当于打败了你正在做的事情的对象。当结果可用时,您总是需要某种回调或某种延续。在C#5中,async / await语法为你做了大量的管道工作,但最终它仍在使用像Task.ContinueWith这样的东西。

如果您对WCF提供的异步操作感到满意,那么在没有涉及TPL并且没有可用的C#5编译器的情况下,您当前使用的模式对我来说是一个好的模式。

在我自己的代码中,我之前构建的东西有点不同 - 使用从线程池上的线程调用的同步WCF操作,其中并发和回调由Reactive Extensions管理。但是,效果大致相同,这完全取决于您想要的语法和概念模型。使用Rx非常适合已经使用大量Rx工具包构建的应用程序,因为它使我们处于IObservable的相同域中,即数据如何移动。

答案 1 :(得分:1)

最简单的方法是使用VS2012重新创建WCF服务代理。这个will change您的异步方法签名就像:

Task<MyProjectList> GetProjectListAsync(string username, string password);

,您的命令变为:

proxy = new SomeService();
this.ProjectList = await proxy.GetProjectListAsync(username, password);

如果您不想重新创建WCF服务代理(它将更新所有您的方法签名),那么您can wrap Begin*/End* methods如下:

public static Task<MyProjectList> GetProjectListTaskAsync(this SomeService @this, string username, string password)
{
  return Task<MyProjectList>.Factory.FromAsync(@this.BeginGetProjectList, @this.EndProjectList, username, password, null);
}

我有一个这种包装的完整示例on my blog

或现有的*Async/*Completed members

public static Task<MyProjectList> GetProjectListTaskAsync(this SomeService @this, string username, string password)
{
  var tcs = new TaskCompletionSource<MyProjectList>();
  EventHandler<GetProjectListCompletedEventArgs> callback = null;
  callback = args =>
  {
    @this.GetProjectListCompleted -= callback;
    if (args.Cancelled) tcs.TrySetCanceled();
    else if (args.Error != null) tcs.TrySetException(args.Error);
    else tcs.TrySetResult(args.Result);
  };
  @this.GetProjectListCompleted += callback;
  @this.GetProjectListAsync(username, password);
}