【问题标题】:Returning a value from async callback in WCF从 WCF 中的异步回调返回值
【发布时间】:2013-02-01 10:51:30
【问题描述】:

我的带有 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 更好?

【问题讨论】:

  • 你用的是VS2012吗?您的目标是什么 .NET 版本?
  • @StephenCleary:是的,我使用的是 VS 2012。目标框架应该至少是 .NET 4。不过 4.5 也可以。

标签: c# wcf mvvm asynchronous


【解决方案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);
}

【讨论】:

  • 我会重新创建所有服务调用以使用Task 实现。谢谢:-)
【解决方案2】:

不幸的是,您不能从这样的异步操作中返回值 - 您必须阻止该线程以等待它完成,这反而会破坏您正在做的事情的对象。当结果可用时,您总是需要某种回调或继续运行。在 C# 5 中,async/await 语法为你做了很多工作,但最终它仍然在后台做这些,使用像 Task.ContinueWith 这样的东西。

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

在我自己的代码中,我之前构建的东西有点不同 - 使用从线程池上的线程调用的同步 WCF 操作,并发和回调由 Reactive Extensions 管理。不过效果大同小异,全看你想要什么样的语法和概念模型。使用 Rx 非常适合已经使用大量 Rx 工具包构建的应用程序,因为它使我们保持在 IObservable 的同一域中,这是数据移动的方式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-13
    相关资源
    最近更新 更多