Я постараюсь объяснить это как можно лучше.Если я упаду на это, тогда я удалю это и продолжу.Может быть, вы, ребята, можете дать мне несколько идей.
Это веб-приложение.(C # 4.0 (сервер) и JQuery 1.6.1 Client)
На стороне клиента у нас настроено несколько текстовых полей, в которых при запуске события .focusout запускается вызов AJAX, отправляющий данныеСлужба WCF.
На стороне сервера мы создали настраиваемое поведение конечной точки, поскольку у нас есть некоторые настроенные средства безопасности, чтобы мы могли получать информацию о пользователях при выполнении этих вызовов AJAX (это прекрасно работает для наспоэтому я не ищу помощи по настройке.)
Проблема возникает, когда я проявляю творческий подход в JQuery, например, если я хочу сказать сразу несколько коробок для обновления (даже если это 2 текстовых поля!)
$(".blahblahClass").focusout(); //fires a bunch of AJAX calls
БУМ .Я получаю System.IndexOutOfRangeException (предполагается, что это ошибка многопоточности) в моем JsonAuthCallContextInitializer ниже, здесь он пытается добавить ключ к этому словарю в BeforeInvoke()
моего CallContextInitializer:
_PrincipalMap[key] = Thread.CurrentPrincipal;
Еговажно упомянуть, что я удаляю ключ из этого самого словаря в AfterInvoke()
ok .. будучи словарем, это, вероятно, не является потокобезопасным.поэтому я добавил lock
с.(что вы увидите в коде ниже)
Я выбрал ReaderWriterLockSlim
, когда прочитал, что lock
имел некоторые проблемы с параллелизмом, а ReaderWriterLock также имел некоторые проблемы.
Поэтому я добавил блокировки (они в коде ниже), используя стандартную LockRecursionPolicy (это означает, что я просто оставил конструктор ReaderWriterLockSlim
пустым).
Когда я снова запустил приложение, Я получил бы LockRecursionException (блокировка чтения не может быть получена с блокировкой записи, удерживаемой в этом режиме)
бросив LockRecursionPolicy.SupportsRecursion
в конструктор ReaderWriterLockSlim
исключение исключено, к сожалению, не всетекстовые поля в моем обновлении веб-страницы ..
Сверху моей головы (что я буду пытаться сегодня), возможно, сделать словарь сам потокобезопасным.Примерно так: Каков наилучший способ реализации многопоточного словаря?
, но я не уверен, что это решит проблему здесь.
Обновление: так что яЯ пробовал пару других вещей.Я решил использовать ConcurrentDictionary и даже решил, что за чертовщина, и избавился от .Remove в AfterInvoke ().Нет кости.В основном это либо подавляет ошибку, и обновляется только одно текстовое поле на странице .html, либо перебивает функцию BeforeInvoke (), если у вас есть несколько текстовых полей, которые обновляют
FYI, статический словарь является преднамеренным
Предложения?(применимый код поведения ниже)
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();
}
}
}