В поисках универсального контекстного хранилища
Прежде всего, я хотел бы, чтобы контекстно-зависимое хранилище было единым для всей среды!
С учетом сказанного я ищу элегантное решение для обеспечения безопасности этих свойств в ASP.NET, WCF и любом другом многопоточном коде.NET. Свойства находятся в некоторых низкоуровневых помощниках трассировки (они предоставляются с помощью методов, если вам интересно, почему они являются внутренними).
Я бы предпочел не зависеть от ненужных сборок (таких как System.Web и т. Д.). Я не хочу требовать, чтобы кто-то использовал этот код для настройки чего-либо. Я просто хочу, чтобы это сработало;) Это может быть слишком высоким порядка, хотя...
У кого-нибудь есть какие-нибудь хитрости в рукавах? (Я видел реализацию Spring)
internal static string CurrentInstance
{
get
{
return CallContext.LogicalGetData(currentInstanceSlotName) as string;
}
set
{
CallContext.LogicalSetData(currentInstanceSlotName, value);
}
}
internal static Stack<ActivityState> AmbientActivityId
{
get
{
Stack<ActivityState> stack = CallContext.LogicalGetData(ambientActivityStateSlotName) as Stack<ActivityState>;
if (stack == null)
{
stack = new Stack<ActivityState>();
CallContext.LogicalSetData(ambientActivityStateSlotName, stack);
}
return stack;
}
}
Обновить
Под безопасным я не подразумеваю синхронизированный. История вопроса здесь
2 ответа
Вот ссылка на (по крайней мере, часть) реализации контекста NHibernate:
https://nhibernate.svn.sourceforge.net/svnroot/nhibernate/trunk/nhibernate/src/NHibernate/Context/
Мне не совсем ясно, где и как это происходит в контексте NHibernate. То есть, если бы я хотел сохранить некоторые значения в "контексте", я бы получил "контекст" из NHibernate и добавил бы мои значения? Я не использую NHibernate, поэтому я не знаю.
Я полагаю, что вы могли бы посмотреть и определить для себя, будет ли вам полезна такая реализация. По всей видимости, идея заключается в том, чтобы внедрить нужную реализацию в зависимости от типа приложения (ASP.NET, WCF и т. Д.). Это, вероятно, подразумевает некоторую конфигурацию (возможно, минимальную, если использовать "MEF" для загрузки "интерфейса ICurrentSessionContext").
Во всяком случае, я нашел эту идею интересной, когда нашел ее некоторое время назад во время поиска информации о CallContext.SetData/GetData/LogicalSetData/LogicalGetData
, Thread.SetData/GetData
, [ThreadStatic]
, так далее.
Кроме того, на основе вашего использования CallContext.LogicalSetData
скорее, чем CallContext.SetData
Я предполагаю, что вы хотите воспользоваться тем фактом, что информация, связанная с логическим потоком, будет распространяться в дочерние потоки, а не просто в "безопасном для потока" месте для хранения информации. Таким образом, если бы вы установили (pr Push) AmbientActivity при запуске вашего приложения, а затем больше не выдвигали никаких действий, любые последующие потоки также были бы частью этой же деятельности, поскольку данные, хранящиеся через LogicalSetData, наследуются дочерними потоками.
Если вы узнали что-то за это время с тех пор, как впервые задали этот вопрос, мне было бы очень интересно услышать об этом. Даже если у вас нет, мне было бы интересно узнать, что вы делаете с контекстом.
В настоящее время я работаю над поддержанием некоторой контекстной информации для ведения журнала / трассировки (аналогично поддержке Trace.CorrelationManager.ActivityId и Trace.CorrelationManager.LogicalOpertionStack и log4net/NLog). Я хотел бы сохранить некоторый контекст (текущее приложение, текущий экземпляр приложения, текущую активность (возможно, вложенную)) для использования в приложении или службе WCF, и я хочу распространить его "автоматически" через границы службы WCF. Это делается для того, чтобы позволить операторам журналирования, зарегистрированным в центральном репозитории, соотноситься с клиентом / действием / и т. Д. Мы могли бы запрашивать и сопоставлять все записи в журнале по конкретному экземпляру конкретного приложения. Операторы регистрации могли быть сгенерированы на клиенте или в одной или нескольких службах WCF.
WCF-распространение ActivityId не обязательно является достаточным для нас, потому что мы хотим распространять (или думаем, что делаем) больше, чем просто ActivityId. Кроме того, мы хотим распространить эту информацию от клиентов Silverlight к службам WCF, а Trace.CorrelationManager недоступен в Silverlight (по крайней мере, не в 4.0, возможно, что-то подобное будет доступно в будущем).
В настоящее время я создаю прототип для распространения нашей "контекстной" информации, используя IClientMessageInspector и IDispatchMessageInspector. Похоже, это будет работать нормально для нас.
Что касается зависимости от System.Web, реализация NHibernate действительно имеет "ReflectiveHttpContext", который использует отражение для доступа к HttpContext, чтобы не было зависимости проекта от System.Web. Очевидно, что System.Web должен быть доступен там, где развернуто приложение, если HttpContext настроен для использования.
Я не знаю, что использование CallContext является правильным шагом, если вы хотите просто обеспечить потокобезопасный доступ к вашим свойствам. Если это так, оператор блокировки - это все, что вам нужно.
Тем не менее, вы должны убедиться, что вы применяете его правильно.
С CallContext вы получите поточно-ориентированный доступ, потому что у вас будут отдельные экземпляры CallContext, когда вызовы поступают в разных потоках (или, скорее, в разных хранилищах). Однако это сильно отличается от создания доступа к ресурсо-ориентированному потоку.
Если вы хотите разделить одно и то же значение между несколькими потоками, то оператор блокировки - это путь. В противном случае, если вам нужны конкретные значения для каждого потока / вызова, используйте CallContext или статические методы GetData/SetData в классе Thread или атрибут ThreadStatic (или любое количество механизмов хранения на основе потоков).