2011-07-28 15 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

是的,我觉得你的问题比这个更深。 –