Может оказаться, что в данный момент исходящий вызов (reqMsg) еще не имеет идентификатора. В таком случае присвоим ему новый еще не использованный идентификатор
bool bClear = false;
if (lcid == null) {
lcid = Identity.GetNewLogicalCalllD();
cctx.RemotingData.LogicalCalllD = lcid;
bClear = true;
}
Здесь статический метод GetNewLogicalCallID класса Identity генерирует новый идентификатор, который и запоминается в контексте вызова.
Если нам пришлось самим построить новый идентификатор для исходящего синхронного вызова, то его нужно и сохранить в свойстве синхронизации. В этом случае условное выражение в следующем операторе if равно true:
bool bTopLevel=false;
if (_property.SyncCallOutLCID==null) {
_property.SyncCallOutLCID = lcid;
bTopLevel = true;
}
Ну а теперь передаем исходящий вызов следующему перехватчику исходящих вызовов и ждем ответа
replyMsg = _nextSink.SyncProcessMessage(reqMsg);
Ну а теперь (после получения ответа) надо за собой почистить (если мы что-то поменяли в контексте вызова и в свойстве синхронизации):
if (bTopLevel) {
_property.SyncCallOutLCID = null;
if (bClear) {
LogicalCallContext cctxRet =
(LogicalCallContext)
replyMsg.Properties[Message.CallContextKey];
cctxRet.RemotingData.LogicalCalllD = null;
}
}
Здесь мы обнуляем свойство SyncCallOutLCID свойства синхронизации (если мы его только что сами установили) и обнуляем идентификатор для полученного ответа replyMsg (если мы сами задавали этот идентификатор для исходящего вызова).
Перехват исходящих асинхронных вызовов
Для обработки исходящих асинхронных вызовов перехватчик использует следующий код:
public virtual IMessageCtrl AsyncProcessMessage(
IMessage reqMsg,
IMessageSink replySink) {
IMessageCtrl msgCtrl = null;
if (!_property.IsReEntrant) {
LogicalCallContext cctx =
(LogicalCallContext)
reqMsg.Properties[Message.CallContextKey];
String lcid = Identity.GetNewLogicalCalllD();
cctx.RemotingData.LogicalCalllD = lcid;
_property.AsyncCallOutLCIDList.Add(lcid);
}
AsyncReplySink mySink =
new AsyncReplySink(replySink, _property);
msgCtrl = _nextSink.AsyncProcessMessage (
reqMsg,
(IMessageSink)mySink);
return msgCtrl;
}
В качестве входных параметров задаются сам вызов reqMsg и перехватчик, на который вызывающая сторона ожидает получения уведомления о завершении процесса выполнения асинхронного вызова.
В случае нереентерабельного контекста исходящему асинхронному вызову назначается новый идентификатор и этот идентификатор сохраняется в списке _asyncLcidList исходящих асинхронных вызовов, поддерживаемому свойством синхронизации (доступ через свойство AsyncCallOutLCIDList). Заметим, что в случае асинхронных вызовов нет нужны сохранять один и тот же идентификатор по всей цепочке вызовов, в связи с чем здесь не проверяется наличие идентификатора, а сразу же назначается новый:
if (!_property.IsReEntrant) {
LogicalCallContext cctx =
(LogicalCallContext)
reqMsg.Properties[Message.CallContextKey];
String lcid = Identity.GetNewLogicalCallID();
cctx.RemotingData.LogicalCalllD = lcid;
_property.AsynCallOutLCIDList.Add(lcid);
}
Зачем вообще сохраняется список идентификаторов исходящих асинхронных вызовов? И почему он обновляется только для нереентерабельного случая?
Единственно, для чего этот список необходим (и именно в случае нереентерабельного контекста) — для определения того, является ли новый входящий вызов вложенным (см. код методов IsNestedCall и HandleWorkRequest). При изложении этого вопроса ранее уже отмечалось наличие некоторых проблем, связанных с определение понятия вложенного вызова и его использования.
Теперь пора отправить исходящий асинхронный вызов следующему перехватчику исходящих асинхронных вызовов. И тут мы должны указать, куда посылать уведомления о завершении вызова. Непосредственно использовать перехватчик уведомлений replySink, предоставленный вызывающей стороной, нельзя, т. к. его непосредственное использование может нарушить логику синхронизации, поддерживаемую в домене синхронизации. В связи с этим создается специальный перехватчик уведомлений mySink, который придерживается логики синхронизации и обеспечивает безопасную работу с replySink:
AsyncReplySink mySink =
new AsyncReplySink(replySink, _property);
Класс AsyncReplySink будет рассмотрен чуть позже.
И вот, наконец, исходящий асинхронный вызов reqMsg передается следующему перехватчику исходящих асинхронных вызовов, а для получения уведомления указывается mySink:
msgCtrl = _nextSink.AsyncProcessMessage (
reqMsg,
(IMessageSink)mySink);
return msgCtrl;
Теперь рассмотрим класс AsyncReplySink:
internal class AsyncReplySink: IMessageSink {
…..
}
В конструкторе в полях _nextSink и _property сохраняются соответственно ссылка на следующий перехватчик (в нашем случае это будет replySink) и ссылка на свойство синхронизации
internal AsyncReplySink(IMessageSink nextsink,
SynchronizationAttribute prop) {
_nextSink = nextSink;
_property = prop;
}
Как и в любом перехватчике, основными методами являются SyncProcessMessage и AsyncProcessMessage.
Вот код для обработки уведомлений, полученных в виде синхронного вызова:
public virtual IMessage SyncProcessMessage (
IMessage reqMsg) {
Workltem work = new WorkItem (
reqMsg,
_nextSink,
null);
_property.HandieWorkRequest(work);
if (!_property.IsReEntrant) {
_property.AsyncCallOutLCIDList.Remove(
((LogicalCallContext)
reqMsg.Properties[Message.
CallContextKey]).
RemotingData.LogicalCallID);
}
return work.ReplyMessage;
}
Мы не можем сразу же послать уведомление на обработку в перехватчик replySink, так как не уверены в том, что он не нарушит логики синхронизации. В связи с этим мы инкапсулируем уведомление в работу
Workltem work = new WorkItem (
reqMsg,
_nextSink,
null);
и обрабатываем его как обычную новую работу, инкапсулирующию синхронный вызов:
_property.HandieWorkRequest(work);
В зависимости от ситуации эта работа будет поставлена в очередь или будет выполняться без задержек, но в любом случае логика синхронизации не будет нарушена. Здесь наш поток блокируется до завершения обработки работы work (включая время простоя в очереди).