C# WCF自定义端点行为中JQuery与字典的并发性问题
我会尽力解释的。如果我在这个问题上失败了,那么我将删除这个并继续前进。也许你们能给我一些想法 这是一个网络应用程序。C 4.0服务器和JQuery 1.6.1客户端 在客户端,我们设置了一组文本框,当触发.focusout事件时,会触发一个AJAX调用,将数据发送到WCF服务 在服务器端,我们创建了一个自定义端点行为,因为我们已经设置了一些安全功能,这样我们可以在这些AJAX调用完成时获取用户信息。这对我们来说很好,所以我不需要安装帮助 当我在JQuery中发挥创意时,问题就来了,比如如果我想告诉一堆框立即更新,即使它们是两个文本框C# WCF自定义端点行为中JQuery与字典的并发性问题,c#,jquery,wcf,dictionary,C#,Jquery,Wcf,Dictionary,我会尽力解释的。如果我在这个问题上失败了,那么我将删除这个并继续前进。也许你们能给我一些想法 这是一个网络应用程序。C 4.0服务器和JQuery 1.6.1客户端 在客户端,我们设置了一组文本框,当触发.focusout事件时,会触发一个AJAX调用,将数据发送到WCF服务 在服务器端,我们创建了一个自定义端点行为,因为我们已经设置了一些安全功能,这样我们可以在这些AJAX调用完成时获取用户信息。这对我们来说很好,所以我不需要安装帮助 当我在JQuery中发挥创意时,问题就来了,比如如果我想告
$(".blahblahClass").focusout(); //fires a bunch of AJAX calls
轰。我在下面的JsonAuthCallContextInitializer中得到一个System.IndexOutOfRangeException,假设它是一个线程错误,在这里,它试图在CallContextInitializer的BeforeInvoke中向该字典添加一个键:
_PrincipalMap[key] = Thread.CurrentPrincipal;
重要的是要提到,我正在从同一个字典中删除一个键
好的。。作为一本字典,这可能不是线程安全的。所以我加了一些锁。您将在下面的代码中看到
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();
}
}
}
我选择ReaderWriterLockSlim,因为我读到锁有一些并发性问题,ReaderWriterLock也有一些问题
因此,我在下面的代码中添加了锁,使用默认的LockRecursionPolicy,这意味着我只是将ReaderWriterLockSlim构造函数留空
当我再次运行应用程序时,我会得到一个LockRecursionException。在这种模式下,如果保持写锁,则可能无法获取读锁
将LockRecursionPolicy.SupportsRecursion放入ReaderWriterLockSlim构造函数会使异常消失,不幸的是,我的网页更新中并不是所有的文本框
我今天要做的就是让字典本身具有线程安全性。大概是这样的:
但我不相信它能解决这里的问题
更新:所以我尝试了一些其他的方法。我决定使用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();
}
}
}
嗯,我阅读了一些文档,我不确定这是否是问题所在,但看起来您在这里遇到了一个问题:
cacheLock.EnterReadLock();
try
{
//LockRecursionException here
_PrincipalMap[key] = Thread.CurrentPrincipal;
}
finally
{
cacheLock.ExitReadLock();
}
它应该是cacheLock.EnterWriteLock和cacheLock.ExitWriteLock,因为您正在添加/更改字典中的值。好吧,我阅读了一些文档,我不确定这是否是问题所在,但看起来您在这里遇到了问题:
cacheLock.EnterReadLock();
try
{
//LockRecursionException here
_PrincipalMap[key] = Thread.CurrentPrincipal;
}
finally
{
cacheLock.ExitReadLock();
}
它应该是cacheLock.EnterWriteLock和cacheLock.ExitWriteLock,因为您正在添加/更改字典中的值。非常好。唉,它没有解决问题。我将它们更改为WriteLock/ExitWriteLock,并得到以下结果:在此模式下不允许递归写入锁获取。将LockRecursionPolicy更改为.SupportsRecursion会给我带来与以前相同的问题,不是所有的文本框都会更新。是的,我认为你的问题比这要深刻得多。很好。唉,它没有解决问题。我将它们更改为WriteLock/ExitWriteLock,并得到以下结果:在此模式下不允许递归写入锁获取。将LockRecursionPolicy更改为.SupportsRecursion会给我带来与以前相同的问题,并不是所有的文本框都会更新。是的,我觉得你的问题要深得多。