What is a context?
It seems to me that a Context class is a control console whose object can invoke any included functions, such as Datacontext and DomainContext in WCF Ria service. Do I understand this concept correctly? If so, in what circumstances do I need to create a context class in my own class hierarchy?
Beside DataContext, what other well-known Context classes does the .net framework have?
![]()
5 Answers 5
You can think about the context as a wrapper for related «things» such as HttpContext, DbContext, ObjectContext. i.e.: HttpContext contains any information you can reach for HTTP related operations.
DbContext contains the methods and properties for database communication. Likewise ObjectContext.
I would say it’s a placeholder or container of related things for something.
![]()
To me, a context object defines a set of values and/or functions that are bound to the current execution path. In other words, just like speaking about a technical topic in the context of a job interview is different than speaking about the same topic at a nerd dinner, the context changes based on factors that affect the runtime environment of the consuming code. That seems abstract, but I can’t think of a better way to describe it at the moment!
Another famous context in .NET is the HttpContext object. Which values will change based on what Http operation is being handled. For example, the url will change in HttpContext.Current.Request.Uri . Hope that puts it in context for you 🙂
A context is commonly a storage mechanism for a group of actions. HttpContext , for example
Encapsulates all HTTP-specific information about an individual HTTP request.
For your WCF example, the «context» is the service. Different services have different contexts. Contexts can be as granular as you want. Some are broad, like the DomainContext , and some are granular, like HttpContext .
Contexts are everywhere, make them when you need to access or set like minded data or functions to things that can be decoupled.
All contexts are like this, they just encapsulate logic for particular action sets.
Here is another post describing the context design pattern.
a Context class is used in some OOP Design patterns, e.g: — State pattern — Strategy pattern
![]()
A context is an ordered sequence of properties that define an environment for the objects resident inside it. Contexts get created during the activation process for objects that are configured to require certain automatic services, such as synchronization, transactions, just-in-time activation, security, and so on. Multiple objects can live inside a context.
A new object’s context is generally chosen based on meta-data attributes on the class. Some important types of context are:
ExecutionContext:
This is the parent context, all the other contexts are a part of it. It is the system that .NET features like Task use to capture and propagate context, but has no behavior of its own.
SecurityContext:
This is where we find any security information that would normally be confined to the current thread. If your code needs to run as a particular user, you may be impersonating that user, or ASP.NET may be doing impersonation for you. In that case, the impersonation is stored in the SecurityContext
CallContext:
This allows the programmer to store custom data that should be available for the lifetime of a logical thread. Although considered bad practice in a lot of situations, it can avoid excessive numbers of method parameters as various context is passed around the program. LogicalCallContext is a related system that works across AppDomains.
Область видимости переменных, константы

Область видимости, или контекст переменной — это часть кода, в пределах которого доступна данная переменная. В общем случае такая область определяется описанными ниже правилами:
Поле, также известное как переменная-член класса, находится в области видимости до тех пор, пока в этой области находится содержащий поле класс.
Локальная переменная находится в области видимости до тех пор, пока закрывающая фигурная скобка не укажет конец блока операторов или метода, в котором она объявлена.
Локальная переменная, объявленная в операторах цикла for, while или подобных им, видима в пределах тела цикла.
Конфликты областей видимости локальных переменных
Использование в больших программах одних и тех же имен переменных в разных частях программы является обычной практикой. Это нормально до тех пор, пока области видимости этих переменных не перекрываются и находятся в совершенно разных частях программы, таким образом исключая любую неоднозначность. Однако следует иметь в виду, что локальные переменные с одним и тем же именем не могут быть объявлены дважды в одном и том же контексте, поэтому вы не сможете поступить так, как показано ниже:
Рассмотрим следующий пример кода:
Важно отметить, что переменная i объявляется в этом коде два раза в пределах одного и того же метода. Это можно делать, поскольку переменные i объявляются в двух отдельных циклах, поэтому каждая из них локальна в пределах собственного цикла.
Вот другой пример:
Если вы попытаетесь скомпилировать это, то получите следующее сообщение об ошибке:
ScopeTest.cs (12,15) : error CS0136: A local variable named ‘3’ cannot be declared in this scope because it would give a different meaning to ‘j’, which is already used in a ‘parent or current’ scope to denote something else
Дело в том, что переменная j, которая определена перед началом цикла for, внутри цикла все еще находится в области видимости и не может из нее выйти до завершения метода Main(). Хотя вторая переменная j (недопустимая) объявлена в контексте цикла, этот контекст вложен в контекст метода Main(). Компилятор не может различить эти две переменных, поэтому не допустит объявления второй из них.
Конфликты областей видимости полей и локальных переменных
В некоторых случаях два идентификатора с одинаковыми именами (хотя и не совпадающими полностью уточненными именами) и одинаковой областью видимости можно различить, и тогда компилятор допускает объявление второй переменной. Причина в том, что C# делает принципиальное различие между переменными, объявленными на уровне типа (полями) и переменными, объявленными в методах (локальными). Рассмотрим следующий фрагмент кода:
Этот код компилируется, несмотря на то, что здесь в контексте метода Main() присутствуют две переменных с именем j: переменная j, определенная на уровне класса и существующая до тех пор, пока не будет уничтожен класс (когда завершится метод Main(), а вместе с ним и программа), и переменная j, определенная внутри Main(). В данном случае новая переменная с именем j, объявленная в методе Main(), скрывает переменную уровня класса с тем же именем. Поэтому когда вы запустите этот код, на дисплее будет отображено число 30.
Константы
Как следует из названия, константа — это переменная, значение которой не меняется за время ее существования. Предваряя переменную ключевым словом const при ее объявлении и инициализации, вы объявляете ее как константу:
Ниже перечислены основные характеристики констант:
Они должны инициализироваться при объявлении, и однажды присвоенные им значения никогда не могут быть изменены.
Значение константы должно быть вычислено во время компиляции. Таким образом, инициализировать константу значением, взятым из другой переменной, нельзя. Если все-таки нужно это сделать, используйте поля только для чтения.
Константы всегда неявно статические. Однако вы не должны (и фактически не можете) включать модификатор static в объявление константы.
Использование констант в программах обеспечивает, по крайней мере, три преимущества:
Константы облегчают чтение программ, заменяя «магические» числа и строки читаемыми именами, назначение которых легко понять.
Константы облегчают модификацию программ. Например, предположим, что в программе C# имеется константа SalesTax (налог с продаж), которой присвоено значение 6 процентов. Если налог с продаж когда-нибудь изменится, вы можете модифицировать все вычисления налога, просто присвоив новое значение этой константе, и не понадобится просматривать код в поисках значений и изменять каждое из них, надеясь, что оно нигде не будет пропущено.
Константы позволяют избежать ошибок в программах. Если попытаться присвоить новое значение константе где-то в другом месте программы, а не там, где она объявлена, компилятор выдаст сообщение об ошибке.
Механизм контекстов в .NET
Вместе с появлением .NET нам была предложено масса полезных и интересных технологий и одна из них – контексты. Естественно, что контексты не являются чем-то принципиально новым. В том же COM они были доступны, начиная с Windows2000, но популярными они не были. Возможно, с появлением .NET контексты будут использоваться более активно. Почему «возможно», а не «наверняка»? Да потому, что большая часть документации пространства имен System.Runtime.Remoting.Contexts содержит только «This type supports the .NET Framework infrastructure and is not intended to be used directly from your code», то есть классы, входящие в это пространство имён, не документированы. Конечно, нельзя сказать, что за все время существования .NET ничего не изменилось. Например, MSDN Magazine уже напечатал две статьи в мартовских выпусках за 2002 и 2003 годы, и очень вероятно, что в марте 2004 выйдет еще одна статья, посвященная ведению лога вызовов на более высоком уровне.
Что такое контексты и зачем они нужны?
Вольный перевод определения контекста из MSDN говорит примерно следующее: «Контекст – это упорядоченный набор свойств, определяющих окружение для объектов, которые исполняются в нем.» Создается контекст на этапе активации объекта, для работы которого нужны те или иные службы (JIT-активация, поддержка транзакций, синхронизация, безопасность и многое другое). Естественно, что в рамках одного контекста может существовать несколько объектов.
Ядром инфраструктуры контекстов является класс Context (пространство имен System.Runtime.Remoting.Contexts). Рассмотрим его основные методы:
| Context Constructor | Конструктор класса. Обычно контексты создаются в процессе активации ContextBoundObject (CBO), но иногда может потребоваться явно создать новый контекст. |
|---|---|
| SetProperty | Установить свойство контекста. |
| GetProperty | Получить свойство контекста. |
| Freeze | Заморозить контекст. «Заморозка» контекста подразумевает, что все нужные свойства контекста уже добавлены, и мы готовы начать его использование. |
| DoCallBack | Сделать обратный вызов в контекст. Этот метод аналогичен методу AppDomain.DoCallBack с единственным отличием, что вызов будет обрабатываться в другом контексте. |
| RegisterDynamicProperty | Регистрация динамического свойства. |
| UnregisterDynamicProperty | Удалить динамическое свойство. |
Есть и несколько других методов, но они не представляют особого интереса (при желании можно посмотреть полный список методов в MSDN).
Как уже упоминалось, использование контекстов тесно связано с классом ContextBoundObject. Это единственный класс (не считая его наследников), для которого всегда создается контекст исполнения. Естественно, встает вопрос – как управлять созданием контекста и наполнением его свойствами?
Наверное, все знают о механизме атрибутов в .NET Framework и о том, как их можно использовать для расширения метаинформации о классе. Контексты в этом случае не исключение. Здесь также активно используются атрибуты для описания дополнительных возможностей. И основным атрибутом здесь выступает ContextAttribute. Обычно этот атрибут используется для управления созданием нового контекста и определения свойств создаваемого контекста. К атрибутам и свойствам мы вернемся чуть позже, а пока рассмотрим, как обеспечивается взаимодействие с CBO.
Работа с CBO в .NET Framework построена на использовании связки TransparentProxy и RealProxy. И здесь программист может столкнуться с уже упоминавшейся трудностью – отсутствием документации. Если RealProxy еще худо-бедно документирован, то с TransparentProxy дела обстоят хуже. В MSDN есть несколько незначительных упоминаний о том, что он существует, и что обычному программисту он не нужен. TP трудно назвать полноценным классом – это, скорее, некий мост между средой исполнения и управляемым кодом. Поэтому легко можно обойтись без документации, так как единственное, что необходимо помнить – это то, что в нем хранится ссылка на RealProxy и некий объект под названием StubData, который используется для принятия решения о том, какой из механизмов вызова методов CBO-объекта будет использоваться (рисунок 1).
Естественно, что это создает один из побочных эффектов – все (sic!) обращения к CBO идут через TransparentProxy (TP), что вносит накладные расходы.
Но это все относится к механизмам работы с ContextBoundObject, а пока мы вернемся к самому началу и рассмотрим механизм создания CBO. Поговорим и о том, на основе какой информации принимается решение о создании для объекта нового контекста или использовании существующего контекста.
Каждый раз, когда исполняемая среда создаёт объект, требующий управляемой активации, она проверяет наличие у этого объекта атрибутов контекста. У этих атрибутов запрашивается два специальных интерфейса – IContextAttribute и IСontextProperty (ContextBoundAttribute реализует оба этих интерфейса). Интерфейс IContextAtrtribute используется для принятия решения о создании нового контекста и наполнении его свойствами, а IContextProperty используется для описания свойств контекста. Активация любого ContextBound-объекта производится в несколько этапов (рисунок 2).
IContextAttribute.IsContextOK – каждый атрибут проверяет пригодность текущего контекста для активации. Если хоть один атрибут возвращает false, то принимается решение о создании нового контекста;
IContextAttribute.GetPropertiesForNewContext – если было принято решение о создании нового контекста, каждый атрибут получает возможность добавить в новый контекст свой список свойств;
IContextProperty.Freeze – каждое свойство уведомляется о том, что инициализация контекста завершена, и оно должно зафиксировать свое состояние;
IContextProperty.IsNewContextOK – после фиксации состояния контекста каждое свойство получает возможность проверить корректность созданного контекста и если, все в порядке, то дальше начинается процесс активации объекта.
Обычно интерфейсы IContextAttribute и IContextProperty напрямую не используются, а используется класс ContextAttribute реализующий оба этих интерфейса.
| СОВЕТ
Естественно, что использование ContextBoundAttribute при объявлении класса не является обязательным требованием. Мы всегда можем воспользоваться классом Activator и указать нужные атрибуты в методе CreateInstance, или самостоятельно создать новый контекст и форсировать активацию CBO именно в нем. Кроме интерфейса IContextProperty каждое свойство может реализовать и несколько дополнительных интерфейсов, каждый из которых так или иначе может влиять на процесс взаимодействия с ContextBoundObject:
На процесс взаимодействия с ContextBoundObject могут оказывать свое влияние и динамические свойства (IDynamicProperty). В отличие от обычных свойств, динамические свойства могут подключаться и отключаться в процессе существования контекста (методы Context.RegisterDynamicProperty и Context.UnregisterDynamicProperty):
Теперь рассмотрим использование свойств, входящих в состав контекста (для простоты будем рассматривать только синхронные вызовы). Начнем мы это делать с серверного контекста. Почему с серверного? Конечно, можно было бы начать и с клиентского, но именно свойства серверного контекста обычно задействуются первыми. Отчего это происходит именно так – достаточно вспомнить с чего начинается исполнение большинства программ: Что получается, когда управление попадает в Main? Естественно, создаётся контекст, используемый по умолчанию, у которого клиентские свойства не определены. Когда будет создан первый ContextBoundObject, то первыми начнут работать свойства именно его (т.е. серверного контекста). Активизация CBO в серверном контекстеБудем считать, что для своего первого CBO мы не поскупились на всевозможные свойства и решили использовать все доступные нам возможности. Что при этом будет происходить, изображено на рисунке 4. Процесс активации объекта начинается в методе DoCrossContextActivation класса ActivationServices (к сожалению, данный класс объявлен как internal, и поэтому его использование сильно затруднено). Первым делом в этом методе создается и инициализируется новый контекст. После этого контекст замораживается (IContextProperty.Freeze), и каждое свойство получает возможность проверить состояние нового контекста (блок IContextProperty на рисунке 2). После проверки контекста инициируется процесс создания «серверного приемника». Для этого каждое свойство проверяется на предмет поддержки интерфейса IContributeServerContextSink и, если такая поддержка обнаруживается, то создается цепочка «приемников», которая завершается специальным приемником ServerContextTerminatorSink. После формирования цепочки «серверных приемников» вызов переходит к первому приемнику в цепочке и, следуя по ней, достигает последнего – ServerContextTerminatorSink, который уведомляет свойства контекста, поддерживающие интерфейс IСontextPropertyActivator, и передает вызов активатору объекта — IConstructionCallMessage.Activator.Activate. Обычно (если никто ничего не поменял на предыдущем шаге) активатором выступает внутренний класс ConstructionLevelActivator, обеспечивающий конструирование объекта и создание его «дипломатических приемников». Так же, как и для «серверных приемников», это происходит через опрос свойств контекста, только в данном случае используется интерфейс IContributeEnvoySink. Что является здесь наиболее важным? Во-первых, получая вызов IContributeServerContextSink.GetServerContextSink, мы можем быть уверены, что создаваемый объект является в этом контексте «первым». Во вторых, именно на этапе активации создается EnvoySink объекта и, хотя вызова SyncProcessMessage для EnvoySink в серверном контексте не будет, именно на этапе активации объект, реализующий EnvoySink, будет отправлен в клиентский контекст (вполне возможно, что его даже придется сериализовать). Естественно, что в процессе серверной активации некоторые свойства оказались незадействованными. Разумеется, не все можно сделать в серверном контексте, и иногда более удобно выполнить часть работы в своем «родном» контексте. Поэтому вернемся и рассмотрим, что же происходило в клиентском контексте до того, как мы начали активацию в серверном. Активация CBO в клиентском контекстеАктивация в клиентском контексте (рисунок 5) начинается после того, как было принято решение о создании нового контекста. Это происходит непосредственно после того, как были опрошены все атрибуты, и один из них (IContextAttribute.IsContextOK) вернул false. Естественно, что еще до начала активации была создана «парочка» TransparentProxy и RealProxy, и именно благодаря действиям RealProxy запускается механизм создания нового контекста (вообще-то, RealProxy — это абстрактный класс, в реальности используется недокументированный класс RemotingProxy) Так или иначе, будем считать, что активация CBO в клиентском контексте начинается с создания цепочки «клиентских приемников». Для этого используются свойства контекста с интерфейсом IContributeClientSink (стоит отметить, что IContributeClientSink используется только один раз, в тот момент, когда управление передается за границу клиентского контекста). После обработки сообщения в клиентском приемнике мы попадаем в ClientContextTerminatorSink, который «уведомляет» динамические свойства контекста и IContextPropertyActivator о вызове. Последним этапом обработки сообщения в клиентском контексте является специальный объект ContextLevelActivator. Именно на этом этапе осуществляется проверка свойств (рисунок 2) вновь созданного серверного контекста – IContextProperty.Freeze & IContextProperty.IsNewContextOK. Но на этом работа со свойствами контекста еще не завершена. Часть свойств обрабатывается при первом вызове метода CBO. Вызов метода CBO в клиентском контекстеКогда рассматривался процесс активизации объекта в серверном контексте, был упомянут специальный приемник – EnvoySink. Как уже говорилось – этот приемник был создан в процессе «серверной» активации и именно он получит шанс первым обработать сообщение на стороне клиента. Такое «поведение» делает “дипломатические” приемники очень удобным инструментом для контроля/модификации входных параметров. Наиболее полезным это может оказаться в распределенных приложениях. В этом случае неверные данные даже не будут переданы на сервер (нужно учитывать, что есть возможность обойти вызов EnvoySink, поэтому наиболее эффективным будет дублирование проверяющего кода как в EnvoySink, так и в ObjectSink). Вызов метода CBO объекта в серверном контекстеВыйдя за пределы клиентского контекста, вызов проходит через уже созданную цепочку серверных приемников и попадает в ServerContextTerminatorSink (рисунок 7). Этот стандартный приемник инициирует создание цепочки «объектных приемников», которая будет завершена приемником ServerObjectTerminatorSink (как и во всех остальных случаях, приемники создаются с помощью свойств контекста, только в данном случае будет использоваться IContributeObjectSink). Что было пропущено на этой диаграмме? Для упрощения можно считать, что вызов методов CBO-объекта осуществляется напрямую из ServerObjectTerminatorSink. В реальности же есть еще один дополнительный приемник – это StackBuilderSink, и именно он отвечает за доставку вызова к методам объекта. Также была пропущена одна интересная возможность – CBO сам может реализовать IMessageSink, и тогда реализация из ServerObjectTerminatorSink не будет использовать StackBuilderSink, а вызовет метод SyncProcessMessage у CBO-объекта.
|