Использование AspNetSynchronizationContext из WCF, размещенного в IIS
Я размещаю свои службы.NET 4.5 WCF в IIS. Существует часть информации, называемая BusinessContext (BC), которая хранится в экземпляре OperationContext.Current, так что любая логика в нисходящем направлении может достичь ее.
Все работало нормально, пока я не ввел async / await, и я столкнулся с этой проблемой. @stephen-cleary упоминал, что ASP.NET использует AspNetSynchronizationContext, дружественный к асинхронности, чтобы сохранить HttpContext.Current в потоках. Поскольку я размещаюсь в IIS, я решил, что смогу воспользоваться преимуществами AspNetSyncCtx в WCF и использовать HttpContext.Current вместо OperationContext для хранения BC.
Я создал сервис WCF с нуля, у которого targetFramework = 4.5, aspnet:UseTaskFriendlySynchronizationContext = true и aspNetCompatibilityEnabled = true, установленный по умолчанию в файле Web.config. Я также добавил AspNetCompatibilityRequirements = Требуется к моей службе.
Во время выполнения я вижу, что HttpContext.Current есть, но SynchronizationContext.Current равен нулю. После ожидания HttpContext становится нулевым, что ожидается из-за отсутствия SyncCtx. Разве это не должно быть установлено в AspNetSyncCtx, когда требуется совместимость asp? Как устанавливается AspNetSyncCtx в ASP.NET?
-- Возможное решение.
Следуя @ Стивену-Клири здесь, я пошел дальше и определил пользовательский SynchronizationContext, чтобы сохранить OperationContext между потоками.
Я хотел бы услышать мнение сообщества относительно этой реализации. Благодарю.
public class OperationContextSynchronizationContext : SynchronizationContext
{
public override void Post(SendOrPostCallback d, object state)
{
OperationContext opCtx = OperationContext.Current;
InternalState internalState = new InternalState()
{
OpCtx = opCtx,
Callback = d,
State = state,
SyncCtx = this
};
ThreadPool.QueueUserWorkItem(new WaitCallback(InternalInvoker), internalState);
}
private void InternalInvoker(object internalState)
{
InternalState internalSt = internalState as InternalState;
SynchronizationContext.SetSynchronizationContext(internalSt.SyncCtx);
using (new OperationContextScope(internalSt.OpCtx))
{
internalSt.Callback.Invoke(internalSt.State);
}
}
private class InternalState
{
public SynchronizationContext SyncCtx { get; set; }
public OperationContext OpCtx { get; set; }
public SendOrPostCallback Callback { get; set; }
public object State { get; set; }
}
}
1 ответ
Я столкнулся с проблемой (ошибка?) Вы упомянули, где HttpContext.Current
не течь с async
код в службе WCF, размещенной в IIS, даже с aspNetCompatiblity
включен и требуется.
Я получил свой проект, используя LogicalCallContext
с CallContext.LogicalSetData
а также CallContext.LogicalGetData
как описано Стивеном Клири в этом сообщении в блоге. Я думаю, что настройка / получение вашего бизнес-контекста, таким образом, будет очень хорошо работать в вашем случае и может быть легче и концептуально проще, чем обычай SynchronizationContext
, В вашем случае вам придется в любом случае установить свой бизнес-контекст... может с таким же успехом установить его на LogicalCallContext
и я верю, что все будет хорошо.
Я объясню свое личное "решение" более подробно, хотя я признаю, что оно немного хакерское, так как я вручную передаю всю HttpContext
объект (и только в методах WCF). Но, опять же, ваш случай с вашим пользовательским контекстным объектом выглядит более подходящим.
В веб-приложении ASP.NET, которое я унаследовал, были звонки HttpContext.Current
замусорил всю бизнес логику (не идеал). Очевидно, что проект работал нормально, пока я не хотел, чтобы несколько методов WCF в приложении были async
,
Многие вызовы в бизнес-логике происходят из-за загрузки страниц ASP.NET и т. Д., Где все работает нормально, как есть.
Я решил (kludged?) Свою проблему в.NET 4.5, создав небольшой вспомогательный класс ContextHelper
и заменил все звонки на HttpContext.Current
с ContextHelper.CurrentHttpContext
,
public class ContextHelper
{
public static void PrepareHttpContextFlow()
{
CallContext.LogicalSetData("CurrentHttpContext", HttpContext.Current);
}
public static HttpContext CurrentHttpContext
{
get
{
return HttpContext.Current ??
CallContext.LogicalGetData("CurrentHttpContext")
as HttpContext;
}
}
}
Теперь в любое время HttpContext.Current
определен, мой вспомогательный метод по сути не работает.
Чтобы это работало, точки входа для моих вызовов WCF должны вызывать PrepareHttpContextFlow
метод перед первым вызовом await
что, по общему признанию, проблематично, и главная причина, по которой я считаю это клуджем.
В моем случае этот недостаток смягчается тем, что у меня есть некоторые ДРУГИЕ вызовы вручную, необходимые для добавления информации логического стека для добавленного контекста регистрации ошибок, который теряется при использовании async
(поскольку информация о физическом стеке в исключении не очень полезна для журналов ошибок в этом случае). Так что, по крайней мере, если я не забуду сделать один из вызовов "подготовиться к асинхронному", я должен не забыть сделать другой:)