簡體   English   中英

在WPF應用程序中使用任務和回調實現自定義異步WCF調用處理時,UI凍結

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM