我觉得必须有一个比我想出的更好的解决方案;这是问题所在:
WPF表单将调用WCF方法,该方法返回bool。调用本身不应该在UI线程上,并且调用的结果将需要显示在表单上,因此返回应该被封送回UI线程。
在这个例子中,我创建了一个“ServiceGateway”类,表单将传递一个在完成Login操作时要执行的方法。网关应使用UI SynchronizationContext调用此Login-complete委托,UI SynchronizationContext在从表单实例化网关时传递。 Login方法使用anon调用_proxy.Login调用。异步委托,然后使用UI SynchronizationContext提供一个回调调用提供给网关的委托('回调'参数)(来自表单):
[CallbackBehavior(UseSynchronizationContext = false)]
public class ChatServiceGateway : MessagingServiceCallback
{
private MessagingServiceClient _proxy;
private SynchronizationContext _uiSyncContext;
public ChatServiceGateway(SynchronizationContext UISyncContext)
{
_proxy = new MessagingServiceClient(new InstanceContext(this));
_proxy.Open();
_uiSyncContext = UISyncContext;
}
public void Login(String UserName, Action<bool> callback)
{
new Func<bool>(() => _proxy.Login(UserName)).BeginInvoke(delegate(IAsyncResult result)
{
bool LoginResult = ((Func<bool>)((AsyncResult)result).AsyncDelegate).EndInvoke(result);
_uiSyncContext.Send(new SendOrPostCallback(obj => callback(LoginResult)), null);
}, null);
}
从表单调用Login方法以响应按钮单击事件。
这很好用,但我怀疑我正在以错误的方式使用Login方法;特别是因为我必须对WCF服务的任何其他方法调用做同样的事情,而且它很难看。
我想保持封装在网关中的异步行为和ui同步。在WCF端实现异步行为会更好吗?基本上我很感兴趣,如果我可以更一般地为其他方法实现上面的代码,或者如果有更好的方法在一起。
答案 0 :(得分:1)
如果您至少定位VS 2012和.NET 4.5,async/await
即可。请注意缺少SynchronizationContext
引用 - 它在await
之前的封面下捕获,并在异步操作完成后发回。
public async Task Login(string userName, Action<bool> callback)
{
// The delegate passed to `Task.Run` is executed on a ThreadPool thread.
bool loginResult = await Task.Run(() => _proxy.Login(userName));
// OR
// await _proxy.LoginAsync(UserName);
// if you have an async WCF contract.
// The callback is executed on the thread which called Login.
callback(loginResult);
}
Task.Run
主要用于将CPU绑定工作推送到线程池,因此上面的示例确实有点滥用它,但如果您不想重写{{1}实现的合同使用基于异步MessagingServiceClient
的方法,它仍然是一个很好的方法。
或.NET 4.0方式(无Task
支持):
async/await
这有点偏离您目前正在做的事情,因为来电者负责确保调用public Task Login(string userName, Action<bool> callback)
{
// The delegate passed to `Task.Factory.StartNew`
// is executed on a ThreadPool thread.
var task = Task.Factory.StartNew(() => _proxy.Login(userName));
// The callback is executed on the thread which called Login.
var continuation = task.ContinueWith(
t => callback(t.Result),
TaskScheduler.FromCurrentSynchronizationContext()
);
return continuation;
}
UI线程,如果他们想要在其上执行回调。但是,这是Login
的标准做法,而 可以保留对您async
或SynchronizationContext
的引用{1}} 强制要在正确的线程上执行回调/继续,它会破坏你的实现和个人(这只是我的意见)我会说这有点代码味道。