Интернет-журнал "Домашняя лаборатория", 2007 №6
Шрифт:
" \n\t <<<IN>>> parameters: (");
for (int i = 0; i < call.ArgCount; i++) {
if (i > 0) _property.LogMessage(", ");
_property.LogMessage(call.GetArgName(i) +
"= " + call.GetArg(i));
}
_property.LogMes sage(")\n");
}
}
_replyMsg = _nextSink.SyncProcessMessage(reqMsg);
if (_replyMsg is IMethodReturnMessage) {
IMethodReturnMessage retMsg =
(IMethodReturnMessage) _replyMsg;
Exception e = retMsg.Exception;
if (e!= null) {
Console.WriteLine(e.Mes sage);
return _replyMsg;
}
lock(_property) {
_property.LogMessage("===" + retMsg.TypeName);
_property.LogMessage("\n" + retMsg.MethodName +
" \n\t <<<OUT»> parameters: (");
for (int i = 0; i < retMsg.OutArgCount; i++) {
if (i > 0) _property.LogMessage(", ");
_property.LogMessage(retMsg.GetOutArgName(i) +
" = " + retMsg.GetOutArg(i));
}
_property.LogMes sage(")\n");
}
}
return _replyMsg;
public virtual IMessageCtrl AsyncProcessMessage(IMessage msg,
IMessageSink replySink) {
throw new InvalidOperationExcept;
}
public IMessageSink NextSink {
get {
return _nextSink;
}
}
}
}
Комментарии
1. Данный код содержит определения двух классов:
? MyCallTraceAttribute
Этот публичный класс доступен всем приложениям, имеющим доступ к сборке MyServer.ехе
? MyCallTraceServerContextSink
Этот класс является внутренним (internal) для сборки MyServer.ехе и не доступен за ее пределами.
2. Классу MyCallTraceAttribute приписан атрибут [AttributeUsage (AttributeTargets. Class)]. Данный атрибут используется при определении пользовательских атрибутов для задания элементов, которым может быть приписан данный атрибут. В данном случае атрибут MyCallTraceAttribute можно приписать только классу (но нельзя приписать, например, какому-то методу).
3. Комментарии к коду класса MyCallTraceAttribute:
? Класс MyCallTraceAttribute является производным классом от класса ContextAttribute и реализует интерфейс IContributeServerContextSink. В свою очередь класс ContextAttribute реализует интерфейсы IContextProperty и IContextAttribute.
Реализация интерфейсов IContextProperty и IContextAttribute обеспечивает выбор контекста для размещения активируемого объекта (в старом или в новом контексте), а в случае формирования нового контекста — назначение ему свойств, которые объект может вызывать в своем коде явно.
Реализация интерфейса IContributeServerContextSink позволяет включить в конец цепочки перехватчиков, которые перехватывают все входящие в контекст вызовы, новый перехватчик. Это позволяет декларативно связать некоторый класс с некоторым автоматическим сервисом, что и является реализацией идей аспектно-ориентированного программирования.
? Константа PROPERTY_NAME будет использована для задания имени свойству контекста. Каждое свойство контекста имеет имя и для любого заданного контекста и имени можно определить — содержит ли данный контекст свойство с данным именем. Эта возможность используется при выяснении пригодности заданного контекста как среды для жизни некоторого объекта с определенными требованиями к наличию автоматических сервисов,
? Поле _logFileName используется свойством контекста для хранения имени файла, в который надо записывать данные о перехваченных вызовах.
? Конструктор атрибута принимает в качестве аргумента имя файла, которое и сохраняется в поле _logFileName. Если имя не задано, генерируется соответствующее исключение. В начале работы конструктора вызывается конструктор базового класса ContextAttribute, которому в качестве аргумента передается имя данного свойства контекста (MyCallTrace).
? Публичный виртуальный метод IsContextOK объявлен в интерфейсе IContextAttribute. Именно этот метод ответственен за определение пригодности заданного контекста _ctx (первый аргумент) для жизни объекта, требования которого заданы в сообщении ms g (второй аргумент).
Сообщение msg должно быть ссылкой на объект, реализующий интерфейс IConstructionCallMessage. Это сообщение представляет запрос на создание некоторого объекта. Тут нужно напомнить, что в .NET удаленные объекты активируются либо сервером, либо клиентом. Сообщение типа IConstructionCallMessage посылается от клиента на сервер именно во втором случае. Для этого клиент либо вызывает new, и тогда все требования относительно активации объекта берутся из конфигурационного файла, либо вызывает Activator.CreateInstance и передает все необходимые данные в аргументах. Это сообщение приходит на сервер, где активатор Activator его его обрабатывает и возвращает клиенту сообщение типа IConstructionReturnMessage. Последнее содержит информацию (objRef), достаточную для построения прокси к активируемому объекту.
Так как при наличии нескольких контекстов вызывающая сторона также получает прокси на объект, активированный в другом контексте, то описанный обмен сообщениями выполняется и в локальном случае при вызове, пересекающем границу контекста.
Базовая реализация метода IsContextOK в классе ContextAttribute (согласно коду из SSCLI) возвращает true, если новый объект не привязан к контексту (IsContextful == false) или у контекста ctx имеется свойство, имя которого совпадает с именем данного свойства. В остальных случаях возвращается false.
В классе MyCallTraceAttribute виртуальный метод IsContextOK переопределяется (overide). Прежде всего генерируется исключение, если не задан контекст ctx или сообщение msg. Далее делается попытка получить в контексте ctx ссылку на его свойство с именем, хранящемся в константе PROPERTY_NAME, типа MyCallTraceAttribute. Если такое свойство контекста находится, и его поле _iogFileName хранит то же имя файла, что и текущее свойство, то контекст ctx признается подходящим для активации в нем экземпляра класса с атрибутом [MyCallTrace (х)], где х — Строка, равная строке, хранящейся _logFileName. В противном случае система выполнения (CLR) создаст новый контекст для активации в нем этого объекта.