[英]UI Freeze when Implementing custom Asynchronous WCF call handling using tasks and callbacks in WPF application
我有一個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);
}
}
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調用的同步用法(我堅持像我的第一個代碼示例那樣完全異步模型),所以我認為它會起作用。 (我仍然不建議這樣做!)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.