Хорошо, я отвечаю сам.
Короткий: решения нет.
Немного подробно:
Проблема в том, что мне нужен способ сохранения последней активной операции для каждого логического контекста. Код трассировки не будет контролировать поток выполнения, поэтому невозможно передать lastStartedOperation в качестве параметра. Контекст вызова может клонироваться (например, если запущен другой поток), поэтому мне нужно клонировать значение как клоны контекста.
CallContext.LogicalSetData () хорошо подходит, но он объединяет значения в исходный контекст по завершении асинхронной операции (фактически заменяя все изменения, сделанные до вызова EndInvoke). Теоретически, это может происходить даже асинхронно, давая непредсказуемый результат CallContext.LogicalGetData ().
Я говорю теоретически, потому что простой вызов a.EndInvoke () внутри asyncCallback не заменяет значения в исходном контексте. Хотя я не проверял поведение удаленных вызовов (и, похоже, WCF вообще не поддерживает CallContext). Кроме того, документация (старая) гласит:
Метод BeginInvoke передает
CallContext на сервер. когда
Вызывается метод EndInvoke,
CallContext сливается обратно на
нить. Это включает случаи, когда
BeginInvoke и EndInvoke называются
последовательно и где BeginInvoke
вызывается в одном потоке и EndInvoke
вызывается функцией обратного вызова.
Последняя версия не так определена:
Метод BeginInvoke передает
CallContext на сервер. Когда
Вызывается метод EndInvoke, данные
содержится в CallContext копируется
вернуться к теме, которая называется
BeginInvoke.
Если вы покопаетесь в источнике фреймворка, вы обнаружите, что значения фактически хранятся внутри хеш-таблицы внутри LogicalCallContext внутри текущего ExecutionContext текущего потока.
Когда вызывается контекстный клон (например, при BeginInvoke) вызывается LogicalCallContext.Clone. И EndInvoke (по крайней мере, когда вызывается внутри исходного CallContext) вызывает LogicalCallContext.Merge (), заменяя старые значения внутри m_Datastore новыми.
Итак, нам нужно как-то указать значение, которое будет клонировано, но не объединено обратно.
LogicalCallContext.Clone () также клонирует (без слияния) содержимое двух приватных полей, m_RemotingData и m_SecurityData. Поскольку типы полей определены как внутренние, вы не можете получить их (даже с помощью emit), добавить свойство MyNoFlowbackValue и заменить значение поля m_RemotingData (или другого) на экземпляр производного класса.
Кроме того, типы полей не являются производными от MBR, поэтому их нельзя обернуть, используя прозрачный прокси.
Вы не можете наследовать от LogicalCallContext - он запечатан. (На самом деле, вы могли бы - если бы использовали API профилирования CLR для замены IL, как это делают фиктивные фреймворки. Не желаемое решение.)
Вы не можете заменить значение m_Datastore, поскольку LogicalCallContext сериализует только содержимое хеш-таблицы, а не саму хеш-таблицу.
Последнее решение - использовать CallContext.HostContext. Это эффективно сохраняет данные в поле m_hostContext LogicalCallContext. LogicalCallContext.Clone () разделяет (не клонирует) значение m_hostContext, поэтому значение должно быть неизменным. Хотя это не проблема.
И даже это не работает, если используется HttpContext, поскольку он устанавливает свойство CallContext.HostContext, заменяющее старое значение. По иронии судьбы, HttpContext не реализует ILogicalThreadAffinative и, следовательно, не сохраняется как значение поля m_hostContext. Он просто заменяет старое значение на ноль.
Итак, решения нет и не будет, так как CallContext является частью удаленного взаимодействия, а удаленное взаимодействие устарело.
PS Thace.CorrelationManager использует CallContext внутри и, следовательно, не работает должным образом.Кстати, LogicalCallContext имеет специальный обходной путь для клонирования стека операций CorrelationManager на клонирование контекста.К сожалению, он не имеет специального обходного пути для слияния.Идеальный!
PPS Образец:
static void Main(string[] args)
{
string key = "aaa";
EventWaitHandle asyncStarted = new AutoResetEvent(false);
IAsyncResult r = null;
CallContext.LogicalSetData(key, "Root - op 0");
Console.WriteLine("Initial: {0}", CallContext.LogicalGetData(key));
Action a = () =>
{
CallContext.LogicalSetData(key, "Async - op 0");
asyncStarted.Set();
};
r = a.BeginInvoke(null, null);
asyncStarted.WaitOne();
Console.WriteLine("AsyncOp started: {0}", CallContext.LogicalGetData(key));
CallContext.LogicalSetData(key, "Root - op 1");
Console.WriteLine("Current changed: {0}", CallContext.LogicalGetData(key));
a.EndInvoke(r);
Console.WriteLine("Async ended: {0}", CallContext.LogicalGetData(key));
Console.ReadKey();
}