c# – 使用WPF应用程序中的任务和回调实现自定义异步WCF调用处理
我有一个
WPF MVVM应用程序.视图模型具有绑定到视图的几个属性,这些属性由来自数据库的数据直接填充,或者通过驻留在视图模型和数据库之间的wcf服务填充.数据连接模式的选择取决于客户端应用程序的App.config文件中的应用程序设置.我想实现我自己的异步调用服务方法并处理它们的返回值的方法.如果我使用Tasks以下列方式实现它,我想知道是否存在线程问题的可能性:
服务呼叫流程: 此外,我有一个MyBusinessClassClient类,它从同一个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.我想避免这种情况.我也想知道这是否是我想要达到的目标的正确方法.我对任务/线程和回调的经验较少,因此我想知道我将来是否会遇到任何问题(线程/内存管理等). 解决方法
@Ananth嘿,我删除了评论,因为第二眼我以为我误读了代码.一般来说,当连接到Web服务时,您应该始终将调用视为异步,因为您可以处理过多的延迟,这会冻结任何线程(通常是GUI线程).如果您需要为单个GUI操作进行多个WCF调用,则会更加复杂.这也会变得更糟,因为您的WCF接口被编写为异步调用,但无论如何都会同步并运行.未来混淆的明确原因.
所以我发现最好只处理异步模型,但至少你可以在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调用的同步用法(我坚持像我的第一个代码示例那样完整的异步模型),所以我认为它会起作用. (我仍然不建议这样做!) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |