2016-10-11 18 views
1

我有一個Web方法上傳事務(ASMX Web服務),它接受XML文件,驗證文件並將文件內容存儲在SQL Server數據庫中。我們注意到某些用戶可以同時提交兩次相同的文件。所以我們可以在我們的數據庫中再次使用相同的代碼(我們不能在數據庫上使用唯一索引,或者在數據庫級別執行任何操作,不要問我爲什麼)。我想我可以使用用戶ID字符串上的鎖定語句,但我不知道這是否會解決問題。或者如果我可以使用兌現對象來存儲所有用戶ID請求,並檢查是否有來自同一用戶ID的2個請求,我們將執行第一個請求並阻止第二個請求,並顯示錯誤消息 ,因此如果有人有任何想法,請幫助阻止來自同一用戶ID的多個請求到Web方法c#

+0

內容是否完全重複?是否允許重複?如果不是,您可以簡單地丟棄重複請求(如果他們提交內容兩次,但_far_足以避免鎖定) –

+0

切勿鎖定字符串。永遠。真。 http://stackoverflow.com/questions/12804879/is-it-ok-to-use-a-string-as-a-lock-object – spender

回答

3

在字符串上阻塞是不好的。阻止你的網絡服務器是不好的。

AsyncLocker是我寫的一個方便的類,它允許鎖定任何類型的行爲很好的字典中的鍵。它還需要異步進入臨界部(相對於鎖的正常封閉的行爲)之前等待:

public class AsyncLocker<T> 
{ 
    private LazyDictionary<T, SemaphoreSlim> semaphoreDictionary = 
     new LazyDictionary<T, SemaphoreSlim>(); 

    public async Task<IDisposable> LockAsync(T key) 
    { 
     var semaphore = semaphoreDictionary.GetOrAdd(key,() => new SemaphoreSlim(1,1)); 
     await semaphore.WaitAsync(); 
     return new ActionDisposable(() => semaphore.Release()); 
    } 
} 

這取決於以下兩個輔助類:

LazyDictionary:

public class LazyDictionary<TKey,TValue> 
{ 
    //here we use Lazy<TValue> as the value in the dictionary 
    //to guard against the fact the the initializer function 
    //in ConcurrentDictionary.AddOrGet *can*, under some conditions, 
    //run more than once per key, with the result of all but one of 
    //the runs being discarded. 
    //If this happens, only uninitialized 
    //Lazy values are discarded. Only the Lazy that actually 
    //made it into the dictionary is materialized by accessing 
    //its Value property. 
    private ConcurrentDictionary<TKey, Lazy<TValue>> dictionary = 
     new ConcurrentDictionary<TKey, Lazy<TValue>>(); 
    public TValue GetOrAdd(TKey key, Func<TValue> valueGenerator) 
    { 
     var lazyValue = dictionary.GetOrAdd(key, 
      k => new Lazy<TValue>(valueGenerator)); 
     return lazyValue.Value; 
    } 
} 

ActionDisposable:

public sealed class ActionDisposable:IDisposable 
{ 
    //useful for making arbitrary IDisposable instances 
    //that perform an Action when Dispose is called 
    //(after a using block, for instance) 
    private Action action; 
    public ActionDisposable(Action action) 
    { 
     this.action = action; 
    } 
    public void Dispose() 
    { 
     var action = this.action; 
     if(action != null) 
     { 
      action(); 
     } 
    } 
} 

現在,如果你保持一個st這個地方的ATIC實例:

static AsyncLocker<string> userLock = new AsyncLocker<string>(); 

你可以用它在async方法,利用LockAsyncIDisposable返回類型的美食寫一個using聲明整齊地包裝了關鍵部分:

using(await userLock.LockAsync(userId)) 
{ 
    //user with userId only allowed in this section 
    //one at a time. 
} 

如果我們在進入之前需要等待,它會異步完成,釋放線程來處理其他請求,而不是阻塞,直到等待結束,並且可能在加載時混亂服務器的性能。

當然,當你需要擴展到多個web服務器時,這種方法將不再起作用,並且你需要使用不同的方法(可能通過數據庫)進行同步。

+0

感謝您的快速回答,讓我試試這個並給我反饋。 – Sharif

+0

實際上,我在這個來自不同客戶端的網絡服務上每秒鐘有大約2000次點擊率。我需要的是阻止從相同的客戶端基於用戶ID的多個請求(所有用戶可以一次擊中網絡方法一次) – Sharif

+0

偉大的答案消費者! –

相關問題