gpt4 book ai didi

c# - 在 WPF 应用程序中使用任务和回调实现自定义异步 WCF 调用处理时 UI 卡住

转载 作者:太空狗 更新时间:2023-10-29 20:34:29 24 4
gpt4 key购买 nike

我有一个 WPF MVVM 应用程序。 View 模型有几个绑定(bind)到 View 的属性,这些属性由直接来自数据库的数据或通过位于 View 模型和数据库之间的 wcf 服务填充。数据连接模式的选择取决于客户端应用程序App.config文件中的应用程序设置。我想实现我自己的方式来异步调用服务方法并处理它们的返回值。我想知道如果我使用任务按以下方式实现线程问题是否有机会:

服务调用流程:ViewModel > ServiceAgent >(MyWCFServiceClient 或 MyBusinessClient)> MyBusinessClass> 数据库为了使用服务操作,我有一个实现 IMyWCFService 的 MyWCFServiceClient 类(在添加服务引用时生成)。

此外,我还有一个从相同的 IMyWCFService 接口(interface)实现的 MyBusinessClassClient 类。因此,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。我想避免这种情况。我也想知道这是否是我想要实现的目标的正确方法。我在任务/线程和回调方面的经验较少,因此我想知道我将来是否会遇到任何问题(线程/内存管理等)。

最佳答案

@Ananth heh,我删除了评论,因为再看一眼我以为我误读了代码。一般来说,当连接到 Web 服务时,您应该始终将调用视为异步调用,因为您可能会处理过多的延迟,这会卡住任何线程(通常是 GUI 线程)。如果您需要为单个 GUI 操作进行多次 WCF 调用,则情况会更加复杂。这也变得更糟,因为您的 WCF 接口(interface)编写得像一个异步调用,但无论如何都会同步运行。 future 困惑的明确原因。

所以我发现最好只处理异步模型,但至少您可以在 WCF 调用中进行类型检查/转换/返回处理。我做了类似的事情,但我仍然会使用回调而不是使用同步调用,但我会让 IAsyncResult WCF 调用中处理,然后将其转换为我期望的类型并将其返回给用户。

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 调用的同步用法(我坚持使用完整的异步模型,就像我的第一个代码示例一样),所以我认为它会起作用. (虽然我仍然不建议这样做!)

关于c# - 在 WPF 应用程序中使用任务和回调实现自定义异步 WCF 调用处理时 UI 卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12743973/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com