2011-07-28 14 views
2

我會盡我所能解釋這一點。如果我摔倒在這,然後我會刪除這個並繼續前進。也許你們可以給我一些想法。在WCF自定義端點行爲中的字典的JQuery併發問題

這是一個網絡應用程序。 (C#4.0(服務器)和JQuery 1.6.1客戶端)

在客戶端,我們有一堆文本框,當觸發.focusout事件時觸發AJAX調用,將數據發送到WCF服務。

在服務器端,我們創建了一個自定義端點行爲,因爲我們有一些安全的東西,所以我們可以在這些AJAX調用時獲取用戶信息(這對我們很好,所以我不尋找設置幫助。)

問題是當我得到創造性的JQuery的,就像如果我想告訴一堆箱子更新一次(哪怕是2個文本框!)

$(".blahblahClass").focusout(); //fires a bunch of AJAX calls 

BOOM。我在JsonAuthCallContextInitializer得到System.IndexOutOfRangeException(假定爲線程錯誤)以下的,在這裏,它嘗試添加一鍵本詞典在我CallContextInitializer的BeforeInvoke()

_PrincipalMap[key] = Thread.CurrentPrincipal; 

它的重要一提的是我從AfterInvoke()

這個非常相同的字典中刪除一個密鑰ok ..作爲一個字典,這可能不是線程安全的。所以我加了一些lock s。 (你會在下面的代碼中看到)

我去了ReaderWriterLockSlim,因爲我看到lock有一些併發問題,ReaderWriterLock也有一些問題。因此,我使用默認的LockRecursionPolicy(意思是我簡單地將ReaderWriterLockSlim構造函數留空)添加了鎖(它們在下面的代碼中)。

當我再次運行應用程序,我會得到一個LockRecursionException(讀鎖可能無法在此模式下保持的寫入鎖被收購)

投擲LockRecursionPolicy.SupportsRecursionReaderWriterLockSlim構造所作的異常走了,可惜,而不是我的網頁更新的所有文本框..

關閉我的頭頂(我今天會嘗試)可能是使字典本身線程安全。這樣的事情:What's the best way of implementing a thread-safe Dictionary?

但我不相信它會解決這裏的事情。

更新:所以我嘗試了其他一些事情。我決定使用一個ConcurrentDictionary,我甚至決定做什麼,並在AfterInvoke()中去掉.Remove。沒有骰子。它基本上要麼抑制的錯誤,並且只有一個的.html頁面上的文本框將更新,或在BeforeInvoke()半身像,如果你有以上幾個文本框更多的更新

僅供參考,靜態字典是故意

建議?(下面適用行爲的代碼)

 public class JsonAuthEndpointBehavior : IEndpointBehavior 
     { 
      private string _key; 

      public JsonAuthEndpointBehavior(string key) 
      { 
       if (key == null) throw new ArgumentNullException("key"); 

       _key = key; 
      } 

      public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher) 
      { 
       var jsonAuthCallContextInitializer = new JsonAuthCallContextInitializer(_key); 

       foreach (var operation in endpointDispatcher.DispatchRuntime.Operations) 
       { 
        operation.CallContextInitializers.Add(jsonAuthCallContextInitializer); 
       } 
      } 

      protected void AddServerErrorHandlers(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) 
      { 
       // Do nothing 
      } 

      public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) 
      { 
       // Do nothing 
      } 

      public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) 
      { 
       // Do nothing 
      } 

      public void Validate(ServiceEndpoint endpoint) 
      { 
       // Do nothing 
      } 
     } 


    public class JsonAuthCallContextInitializer : ICallContextInitializer 
    { 
     private readonly string _key; 
     private static readonly Dictionary<int, IPrincipal> _PrincipalMap = new Dictionary<int, IPrincipal>(); 
     private readonly ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); 

     public JsonAuthCallContextInitializer(string key) 
     { 
      if (key == null) throw new ArgumentNullException("key"); 

      _key = key; 
     } 

     public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message) 
     { 
      if (WebOperationContext.Current == null) 
       throw new NotSupportedException("JSON Authentication call context initializer requires HTTP"); 

      if (Thread.CurrentPrincipal != null) 
      { 
       var key = Thread.CurrentThread.ManagedThreadId; 

       cacheLock.EnterReadLock(); 
       try 
       { 
        //LockRecursionException here 
        _PrincipalMap[key] = Thread.CurrentPrincipal; 
       } 
       finally 
       { 
        cacheLock.ExitReadLock(); 
       } 
      } 

      Thread.CurrentPrincipal = new ClaimsPrincipal(new[] 
      { 
       new ClaimsIdentity((from c in x.Claims select new Claim(blahblah.ToString())).ToArray()) 
      }); 

      return null; 
     } 

     public void AfterInvoke(object correlationState) 
     { 
      var key = Thread.CurrentThread.ManagedThreadId; 

       cacheLock.EnterReadLock(); 
       try 
       { 
        if (!_PrincipalMap.ContainsKey(key)) return; 

        Thread.CurrentPrincipal = _PrincipalMap[key]; 
       } 
       finally 
       { 
        cacheLock.ExitReadLock(); 
       } 

       cacheLock.EnterWriteLock(); 
       try 
       { 
        _PrincipalMap.Remove(key); 
       } 
       catch (Exception) 
       { 
        cacheLock.ExitWriteLock(); 
       } 
     } 
    } 

回答

0

嗯,我讀了一點的文件,我不知道如果這是問題,但它看起來像你這裏有一個問題:

  cacheLock.EnterReadLock(); 
      try 
      { 
       //LockRecursionException here 
       _PrincipalMap[key] = Thread.CurrentPrincipal; 
      } 
      finally 
      { 
       cacheLock.ExitReadLock(); 
      } 

它應該是cacheLock.EnterWriteLock和cacheLock.ExitWriteLock,因爲您正在添加/更改字典中的值。

+0

非常好的一點。唉,它並沒有解決問題。我將它們更改爲WriteLock/ExitWriteLock,並得到以下結果:「在此模式下不允許遞歸寫入鎖定採集。」將LockRecursionPolicy更改爲.SupportsRecursion給了我之前的同樣的問題,並非所有的文本框都會更新。 – KevinDeus

+0

是的,我覺得你的問題比這個更深。 –