Поведение, проиллюстрированное вашим примером, действительно задумано. LogicalCallContext может проходить в двух направлениях через асинхронный вызов или удаленный вызов .net. Когда вы вызываете EndInvoke, LogicalCallContext дочернего контекста объединяется обратно с родительским, как вы заметили. Это сделано намеренно, чтобы вызывающие удаленные методы могли получить доступ к любым значениям, установленным удаленным методом. Вы можете использовать эту функцию для передачи данных назад от ребенка, если хотите.
Отладка с помощью пошагового перехода на .NET Framework, есть явные комментарии на этот счет:
в System.Runtime.Remoting.Proxies.RemotingProxy.Invoke:
case Message.EndAsync:
// This will also merge back the call context
// onto the thread that called EndAsync
RealProxy.EndInvokeHelper(m, false);
в System.Runtime.Remoting.Proxies.RealProxy.EndInvokeHelper:
// Merge the call context back into the thread that
// called EndInvoke
CallContext.GetLogicalCallContext().Merge(
mrm.LogicalCallContext);
Если вы хотите избежать слияния данных, это довольно легко пропустить, просто избегайте вызова EndInvoke из основного потока. Например, вы можете использовать ThreadPool.QueueUserWorkItem, который будет передавать LogicalCallContext в , но не в него, или вызывать EndInvoke из AsyncCallback.
Если посмотреть на пример на сайте Microsoft Connect, причина того, что значение LogicalSetData не возвращается из вызова RunWorkerCompleted, заключается в том, что BackgroundWorker не передает контекст обратно. Кроме того, помните, что LogicalSetData - это не то же самое, что локальное хранилище потока, поэтому не имеет значения, что RunWorkerCompleted выполняется в потоке пользовательского интерфейса - LogicalCallContext все еще является дочерним контекстом, и если родительский процесс явно не передает его обратно вызывая EndInvoke из порождающего потока, он будет отменен. Если вы хотите локальное хранилище потока, вы можете получить к нему доступ из Thread, например, так:
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Thread.SetData(Thread.GetNamedDataSlot("foo"), "blah!!");
}
private void button1_Click(object sender, EventArgs e)
{
var val = (string)Thread.GetData(Thread.GetNamedDataSlot("foo"));
MessageBox.Show(val ?? "no value");
}
В этом примере выскакивает MessageBox с надписью "бла !!" Причина в том, что оба обратных вызова выполняются в потоке пользовательского интерфейса, поэтому имеют доступ к одному и тому же локальному хранилищу потока.
Надеюсь, это поможет прояснить ситуацию.