服务呼叫流程:
viewmodel> ServiceAgent> (MyWCFServiceClient或MyBusinessClient)> MyBusinessClass>数据库
为了使用服务操作,我有一个MyWCFServiceClient类,它实现了IMyWCFService(在添加服务引用时生成).
此外,我有一个MyBusinessClassClient类,它从同一个IMyWCFService接口实现.
因此,MyWCFService和MyBusinessClient都具有相同的方法签名.我选择在生成服务客户端时不生成任何异步方法,因为,如果我这样做,我可能需要在MyBusinessClient中实现由IMyWCFService生成的许多不必要的东西.
假设我有一个方法GetEmployee(int id),它返回一个在IMyWCFService中定义的Employee对象.因此,MyWCFServiceClient和MyBusinessClient类都将具有其实现.
在我的viewmodel中,我有:
private void btnGetEmployee_Click() { ServiceAgent sa = new ServiceAgent (); //this call/callback process the service call result sa.GetEmployee(1673,(IAsyncResult ar) => { Task<Employee> t1 = (Task<Employee>)ar; Employee = t1.Result; //do some other operation using the result //do some UI updation also }); } //this property is bound a label in the view private Employee _employee; public Employee Employee { get { return _ employee; } set { _ employee = value; OnPropertyChanged(() => Employee); } }
ServiceAgent类实现如下:
public class ServiceAgent { private IMyWcfService client; public ProxyAgent() { //The call can go to either MyWCFServiceClient or //MyBusinessClient depending on this setting //client = new MyBusinessClient(); //OR client = new MyWcfServiceClient(); } public void GetEmployee(int id,AsyncCallback callback) { //My implementation to execute the service calls asynchronously using tasks //I don’t want to use the complex async mechanism generated by wcf service reference ;) Task<Employee> t = new Task<Employee>(()=>client.GetEmployee(id)); t.Start(); try { t.Wait(); } catch (AggregateException ex) { throw ex.Flatten(); } t.ContinueWith(task=>callback(t)); } }
这冻结了我的UI.我想避免这种情况.我也想知道这是否是我想要达到的目标的正确方法.我对任务/线程和回调的经验较少,因此我想知道我将来是否会遇到任何问题(线程/内存管理等).
解决方法
所以我发现最好只处理异步模型,但至少你可以在WCF调用中进行类型检查/转换/返回处理.我做了类似的事情,但我没有使用同步调用,我仍然会使用回调,但我会在WCF调用中处理IAsyncResult,然后将其转换为我期望的类型并将其提供给用户.
public void GetEmployee(int id,Action<Employee> getEmployeeCompletedHandler) { Task<Employee> t = new Task<Employee>(()=>client.GetEmployee(id)); t.Start(); t.ContinueWith(task=> { if (getEmployeeCompletedHandler != null) getEmployeeCompletedHandler(t1.Result); }); }
这使您的典型用法:
sa.GetEmployee(1673,result => this.Employee = result);
如果你真的想要维护一个同步模型,那么你可以将工作转移到后台线程(但是从GUI线程的角度来看仍然是“异步”).此时,您也可以让GetEmployee方法同步并返回值.这种方式对于使用它的API使用者来说显而易见的是没有异步操作:
public Employee GetEmployee(int id) { Task<Employee> t = new Task<Employee>(()=>client.GetEmployee(id)); t.Start(); try { t.Wait(); } catch (AggregateException ex) { throw ex.Flatten(); } return t.Result; }
//spawn a background thread to prevent the GUI from freezing BackgroundThread.Spawn(() => { this.Employee = sa.GetEmployee(1673); });
注意,BackgroundThread是一个自定义类,您可以用它来包装后台线程的创建/生成.我将把这个实现细节留给你,但我发现最好只有一个托管的线程包装器,因为它使用得更简单并抽象实现细节(使用线程池?新线程?BackgroundWorker?谁在乎!)
只是注意,我没有尝试过上面刚刚发布的WCF调用的同步用法(我坚持像我的第一个代码示例那样完整的异步模型),所以我认为它会起作用. (我仍然不建议这样做!)