Pregunta

Tengo un código por igual

void ExecuteTraced(Action a, string message)
{
    TraceOpStart(message);
    a();
    TraceOpEnd(message);
}

La devolución de llamada (a) podría llamar ExecuteTraced de nuevo, y, en algunos casos, de forma asíncrona (a través de ThreadPool, BeginInvoke, PLINQ etc, por lo que no tengo capacidad de alcance operación explícitamente marca). Quiero rastrear toda la operación anidada (incluso si llevan a cabo de forma asíncrona). Por lo tanto, necesito la capacidad de conseguir la última operación de trazado en el interior contexto de la llamada lógica (puede haber una gran cantidad de hilos concurrentes, por lo que es imposible el uso lastTraced campo estático).

No tenemos CallContext.LogicalGetData y CallContext.LogicalSetData, pero, por desgracia, se propaga LogicalCallContext cambios de nuevo en el contexto de los padres como EndInvoke () llamada. Lo que es peor, esto puede aparecer dentro en cualquier momento si EndInvoke () se llama asíncrono. EndInvoke cambia CallContext actual - ¿por qué

Además, hay Trace.CorrelationManager, pero basado en CallContext y tienen todos los mismos problemas.

Hay una solución: utilizar la propiedad CallContext.HostContext que no se propaga de vuelta al terminar la operación asíncrona. Además, does'nt clon, por lo que el valor debe ser inmutable - no es un problema. Sin embargo, es utilizado por HttpContext y así, solución no es utilizable en aplicaciones ASP.NET.

La única manera que veo es envolver HostContext (si no la mía) o todo LogicalCallContext en dinámica y enviar todas las llamadas al lado de la última operación de trazado.

¿Fue útil?

Solución

Ok, Estoy respondiendo a mí mismo.

Corto uno:. no existe una solución

Ligeramente detallada:

El problema es, necesito una manera de almacenar última operación activo por cada contexto lógico. Rastreo de código will've ningún control sobre el flujo de ejecución, por lo que es imposible pasar lastStartedOperation como parámetro. contexto de llamada puede clonar (por ejemplo, si otro hilo iniciado), así que necesito para clonar el valor como clones de contexto.

CallContext.LogicalSetData () trajes bien, pero fusionando los valores en el contexto original como operación asíncrona terminado (en efecto, en sustitución de todos los cambios realizados antes EndInvoke llama). Theortically, que puede aparecer dentro incluso de forma asíncrona, dando resultado impredecible de CallContext.LogicalGetData ().

Digo en teoría porque simple llamada a.EndInvoke () dentro de un AsyncCallback no pretende sustituir los valores en el contexto original. Sin embargo, no me registro comportamiento de interacción remota llamadas (y parece, WCF no respeta CallContext en absoluto). Además, el documentación (una edad) dice:

  

pasa método El BeginInvoke la   CallContext al servidor. Cuando   EndInvoke método se llama, el   CallContext está de vuelta en el fusionada   hilo. Esto incluye los casos en que   BeginInvoke y EndInvoke se llaman   secuencial y donde BeginInvoke es   llama en un hilo y EndInvoke es   llamada a una función de devolución de llamada.

Última versión no es tan definida:

  

pasa método El BeginInvoke la   CallContext al servidor. Cuando el   método EndInvoke se llama, los datos   contenida en el CallContext es copiaron   de nuevo en el hilo que llama   BeginInvoke.

Si digg en la fuente de marco, se encuentra que los valores se almacenan realmente dentro de una tabla hash en el interior LogicalCallContext dentro ExecutionContext actual del subproceso actual.

Cuando clones contexto de llamada (por ejemplo en BeginInvoke) LogicalCallContext.Clone llama. Y EndInvoke (al menos cuando se le llama en el interior CallContext originales) llama LogicalCallContext.Merge () la sustitución de los viejos valores dentro m_Datastore por otras nuevas.

Así que necesitamos de alguna manera proporcionar el valor que se clonará pero no ha incluido de nuevo.

LogicalCallContext.Clone () también clones (sin fusión) el contenido de dos campos privados, m_RemotingData y m_SecurityData. Como tipos del campo definido como interno, no se podía derivar de ellos (incluso con emiten), añadir MyNoFlowbackValue propiedad y reemplazar m_RemotingData (u otro) el valor del campo con instancia de la clase derivada.

Además, los tipos de campo son no deriva de MBR, así que es imposible para envolverlos usando proxy transparente.

No se pudo heredar de LogicalCallContext - se selló. (N. B. En realidad, se podría -.. Si se utiliza CLR perfiles API para reemplazar IL como marcos simulados No una solución deseada)

No se podía sustituir el valor m_Datastore, porque LogicalCallContext serializa sólo el contenido de la tabla hash, no la tabla hash en sí.

Última solución es utilizar CallContext.HostContext. Esto efectivamente almacena los datos en el campo de la m_hostContext LogicalCallContext. LogicalCallContext.Clone () acciones (no clones) el valor de m_hostContext, por lo que el valor debe ser inmutable. No es un problema sin embargo.

E incluso si esto falla HttpContext utilizado, ya que establece la propiedad CallContext.HostContext la sustitución de su antiguo valor. Irónicamente, HttpContext no implementa ILogicalThreadAffinative, y por lo tanto no se almacena como valor del campo m_hostContext. Simplemente reemplaza el valor antiguo con nula.

Por lo tanto, no hay solución y nunca será, como CallContext es la parte de interacción remota y comunicación remota es obsoleto.

P.S. Thace.CorrelationManager utiliza CallContext internamente y por lo tanto no funciona como se desea, también. Por cierto, LogicalCallContext tiene solución especial para pila operación de clonación de CorrelationManager el clon de contexto. Por desgracia, no tiene solución no es especial en combinación. Perfecto!

P.P.S. La muestra:

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();
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top