2011-01-21 23 views
9

是否有理由讓每個WCF服務調用異步?是否有理由讓每個WCF調用異步?

我和我的搭檔進行了討論。他希望每個WPF服務調用Async以避免鎖定UI(它是桌面WPF應用程序)。我反對這個想法。在大多數情況下,我不覺得需要Async調用,並且在需要的時候,RequestingClass和DataManager都應該專門編碼以處理異步調用。

我的理由是,這是更多的代碼來設置回調的一切,它是非常混亂。我也認爲這可能會導致性能下降,儘管我還沒有證實這一點。他的觀點是,有時候你會收回大量數據,並且會鎖定用戶界面,並且沒有太多的工作來設置這樣的WCF調用(他也沒有發現下面的代碼令人困惑)。

我們之前都從未與WCF服務器合作過,所以我想我會給他帶來懷疑的好處,並在此詢問其他意見。

例如:

我的方式:

public override User GetById(int id) 
{ 
    return new User(service.GetUserById(id)); 
} 

它可以鎖定用戶界面,UserDataManager和WCF服務通道,直到與用戶DataTransferObject的WCF服務器的回報,但很容易理解並快速編碼。它將用於大多數WCF服務調用,除非實際上預計會延遲獲取數據,在這種情況下,DataManager將設置爲處理異步調用。

他的方式:在基類

public override void GetById(int id, Action<UserGroup> callback = null) 
{ 
    // This is a queue of all callbacks waiting for a GetById request 
    if (AddToSelectbyIdQueue(id, callback)) 
     return; 

    // Setup Async Call 
    var wrapper = new AsyncPatternWrapper<UserDTO>(
     (cb, asyncState) => server.BeginGetUserById(id, cb, asyncState), 
     Global.Instance.Server.EndGetUserById); 

    // Hookup Callback 
    wrapper.ObserveOnDispatcher().Subscribe(GetByIdCompleted); 

    // Run Async Call 
    wrapper.Invoke(); 
} 

private void GetByIdCompleted(UserDTO dto) 
{ 
    User user = new User(dto); 

    // This goes through the queue of callbacks waiting 
    // for this method to complete and executes them 
    RunSelectIdCallbacks(user.UserId, user); 
} 

回調隊列:

/// <summary> 
/// Adds an item to the select queue, or a current fetch if there is one 
/// </summary> 
/// <param name="id">unique object identifier</param> 
/// <param name="callback">callback to run</param> 
/// <returns>False if it needs to be fetched, True if it is already being 
/// fetched</returns> 
protected virtual bool AddToSelectbyIdQueue(int id, Action<T> callback) 
{ 
    // If the id already exists we have a fetch function already going 
    if (_selectIdCallbacks.ContainsKey(id)) 
    { 
     if(callback != null) 
      _selectIdCallbacks[id].Add(callback); 
     return true; 
    } 

    if (callback != null) 
    { 
     List<Action<T>> callbacks = new List<Action<T>> {callback}; 
     _selectIdCallbacks.Add(id, callbacks); 
    } 

    return false; 
} 

/// <summary> 
/// Executes callbacks meant for that object Id and removes them from the queue 
/// </summary> 
/// <param name="id">unique identifier</param> 
/// <param name="data">Data for the callbacks</param> 
protected virtual void RunSelectIdCallbacks(int id, T data) 
{ 
    if (_selectIdCallbacks.ContainsKey(id)) 
    { 
     foreach (Action<T> callback in _selectIdCallbacks[id]) 
      callback(data); 

     _selectIdCallbacks.Remove(id); 
    } 
} 

它不會鎖定用戶界面時,DataManager的,或WCF服務渠道,但是很多額外的編碼進入它。

無論如何,AsyncPatternWrapper都在我們的應用程序中。它是什麼,使我們能夠異步調用WCF和訂閱回調事件

編輯 我們確實有一個包裝,我們可以從UI線程中使用包裝DataManager的任何電話。它在BackgroundWorker上執行Synchronous方法,並對結果執行回調。

大多數額外的代碼是爲了防止鎖定DataManager和WCF服務通道。

+3

考慮評估新C#「異步/等待」功能的CTP版本。它旨在使寫這種代碼變得更容易。我們很樂意在異步論壇上獲得您的反饋。 http://msdn.microsoft.com/en-us/vstudio/async.aspx – 2011-01-21 18:40:42

+0

@Eric:謝謝,我正在調查它,到目前爲止我很喜歡它! – Rachel 2011-01-21 19:01:26

+0

@Eric:你能指點我一些關於在WCF服務中使用它的好教程/演練嗎? – Rachel 2011-01-21 21:09:44

回答

6

你的伴侶是正確的;你不應該阻止UI線程。

作爲異步調用的替代方法,您還可以使用BackgroundWorker或ThreadPool在後臺線程中進行同步調用。

2

值得注意的幾件事情:

  1. 任何WCF通話有可能阻塞,直到它的超時時間,你應該考慮到這一點。如果在連接丟失的情況下阻止用戶界面,它會導致用戶體驗不佳。如果服務器停滯不前,調用也可能比平常運行速度慢。

  2. 即使您不打包異步目的的調用,您可能已經想要調用代理的一些包裝。這是因爲代理上的任何調用都可能使通道處於故障狀態,並且如果發生這種情況,您可以通過在通道上調用Abort(),否則可能會泄漏資源。有關其他信息,請參閱this postthis other post。 WCF代理不能像普通類一樣使用,如果你想在生產場景中使用WCF代理,它必然會包含額外的包裝。這在很大程度上是不可避免的,因爲這是遠程通信要求引入的額外邊界情況和不可預知行爲的結果。