2014-02-12 23 views
1

我有一個Windows Phone手機客戶端與皮膚切換按鈕,它充當「最喜歡的」按鈕。然後,checked屬性將雙向綁定到ViewModel(標準MVVM模式)。使用信號量的異步切換按鈕,以避免服務負荷

<ToggleButton IsChecked="{Binding DataContext.IsFavouriteUser, ElementName=PageRoot, Mode=TwoWay}"> 

當綁定的布爾值發生變化時,我想啓動一個到服務的異步網絡調用。

public bool IsFavouriteUser 
    { 
     get { return _isFavouriteUser; } 
     set 
     { 
      if (SetProperty(ref _isFavouriteUser, value)) 
      { 
       // Dispatches the state change to a RESTful service call 
       // in a background thread. 
       SetFavouriteState(); 
      } 
     } 
    } 

如果用戶按下按鈕多次,那麼很多添加/刪除異步服務電話可以作出 - 可以想像,這些需要2秒做網絡往返和業務處理。

在過去,我已經使用類似:

private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1); 

    // I would probably dispatch this call to a background thread in the real client 
    public async Task<bool> SetFavouriteState() 
    { 
     try 
     { 
      await _semaphore.WaitAsync(); 

      bool result; 

      if (IsFavouriteUser) 
      { 
       result = await ServiceClient.AddAsync(x); 
      } 
      else 
      { 
       result = await ServiceClient.RemoveAsync(x); 
      } 

      return result; 
     } 
     catch 
     { 
      // I wouldn't use an empty catch in production code 
      return false; 
     } 
     finally 
     { 
      _semaphore.Release(); 
     } 
    } 

然而,這可能無休止地排隊用戶輸入;而服務僅對最新的用戶事件感興趣 - 開啓或關閉 - UI應該對用戶輸入保持響應。

  • 如果用戶重複點擊按鈕,確保客戶端不發送「添加/刪除/添加/刪除」的最佳方法是什麼?即我想忽略中間兩個事件,只發送「添加,等待響應完成,刪除」。
  • 有沒有更好的方式來以異步的方式綁定到這個布爾屬性?
  • 什麼是鎖定我的模型的最佳方式,以便在這種情況下只有一個請求在任何時間點都在進行?
  • 什麼是最好的方式告知用戶,當我們等待電話發生(也許失敗)時發生了什麼?

回答

2

有幾種很好的模式可以處理異步重入問題,即如果用戶操作在已經處於運行中時調用異步方法會發生什麼情況。我寫在這裏的一些模式的文章:

http://blogs.msdn.com/b/lucian/archive/2014/03/03/async-re-entrancy-and-the-patterns-to-deal-with-it.aspx

我覺得你的問題是模式5(代碼如下所示)的一個特例。

但是,請注意您的規格中有一個奇怪的地方。有可能用戶點擊的速度足夠快,以便獲得序列「添加」和「添加」(例如,如果插入的「刪除」沒有機會在第二次單擊「添加到達」之前開始執行)。所以請以您自己的特定情況來防止這種情況。

async Task Button1Click() 
{ 
    // Assume we're being called on UI thread... if not, the two assignments must be made atomic. 
    // Note: we factor out "FooHelperAsync" to avoid an await between the two assignments. 
    // without an intervening await. 
     if (FooAsyncCancellation != null) FooAsyncCancellation.Cancel(); 
     FooAsyncCancellation = new CancellationTokenSource(); 
     FooAsyncTask = FooHelperAsync(FooAsyncCancellation.Token); 

     await FooAsyncTask; 
} 

Task FooAsyncTask; 
CancellationTokenSource FooAsyncCancellation; 

async Task FooHelperAsync(CancellationToken cancel) 
{ 
     try { if (FooAsyncTask != null) await FooAsyncTask; } 
     catch (OperationCanceledException) { } 
    cancel.ThrowIfCancellationRequested(); 
     await FooAsync(cancel); 
} 

async Task FooAsync(CancellationToken cancel) 
{ 
    ... 
} 
0

我會建議禁用ToggleButton按鈕,並表示當請求被解僱不確定ProgressBar和隱藏ProgressBar並啓用ToggleButton當它完成。