Ana*_*tha 7 c# wpf wcf asynchronous task-parallel-library
我有一个WPF MVVM应用程序.视图模型具有绑定到视图的几个属性,这些属性由来自数据库的数据直接填充,或者通过驻留在视图模型和数据库之间的wcf服务填充.数据连接模式的选择取决于客户端应用程序的App.config文件中的应用程序设置.我想实现我自己的异步调用服务方法并处理它们的返回值的方法.如果我使用Tasks以下列方式实现它,我想知道是否存在线程问题的可能性:
服务调用流:ViewModel> ServiceAgent>(MyWCFServiceClient或MyBusinessClient)> MyBusinessClass> Database Inorder使用服务操作我有一个实现IMyWCFService的MyWCFServiceClient类(在添加服务引用时生成).
此外,我有一个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);
}
}
Run Code Online (Sandbox Code Playgroud)
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));
}
}
Run Code Online (Sandbox Code Playgroud)
这冻结了我的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);
});
}
Run Code Online (Sandbox Code Playgroud)
这就是你的典型用法:
sa.GetEmployee(1673, result => this.Employee = result);
Run Code Online (Sandbox Code Playgroud)
如果您确实想维护同步模型,那么您可以将工作移至后台线程(但从 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;
}
Run Code Online (Sandbox Code Playgroud)
那么你的调用代码可能如下所示:
//spawn a background thread to prevent the GUI from freezing
BackgroundThread.Spawn(() =>
{
this.Employee = sa.GetEmployee(1673);
});
Run Code Online (Sandbox Code Playgroud)
注意,BackgroundThread是一个自定义类,您可以使用它来包装后台线程的创建/生成。我将把实现细节留给您,但我发现最好有一个用于线程的托管包装器,因为它使使用变得更加简单并抽象实现细节(使用线程池?新线程?BackgroundWorker?谁在乎!)
请注意,我还没有尝试过上面刚刚发布的 WCF 调用的同步用法(我坚持使用完整的异步模型,就像我的第一个代码示例一样),所以我认为它会起作用。(不过我仍然不建议这样做!)
| 归档时间: |
|
| 查看次数: |
1756 次 |
| 最近记录: |