Question

J'ai un code aussi bien

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

Le rappel (a) pourrait appeler ExecuteTraced à nouveau, et, dans certains cas, de manière asynchrone (via ThreadPool, BeginInvoke, PLINQ etc, donc je n'ai pas la capacité de champ d'opération marque explicitement). Je veux tracer toutes les opérations imbriquées (même si elles exécutent de manière asynchrone). Donc, j'ai besoin la possibilité d'obtenir la dernière opération tracée dans le contexte d'appel logique (il peut y avoir beaucoup de threads simultanés, il est donc impossible d'utiliser lastTraced champ statique).

Il sont CallContext.LogicalGetData et CallContext.LogicalSetData, mais malheureusement, LogicalCallContext rebascule se propage au contexte parent comme EndInvoke () appelé. Pire encore, cela peut occure à tout moment si EndInvoke () a été appelée async. EndInvoke change CallContext actuel - pourquoi

En outre, il y a Trace.CorrelationManager, mais basé sur CallContext et ont tous les mêmes problèmes.

Il y a une solution de contournement: utiliser la propriété CallContext.HostContext qui ne se propage pas en arrière que le fonctionnement async a pris fin. , Il does'nt également clone, de sorte que la valeur doit être immuable - pas un problème. Cependant, il est utilisé par HttpContext et ainsi, solution de contournement n'est pas utilisable dans les applications Asp.Net.

La seule façon que je vois est d'envelopper HostContext (si pas le mien) ou LogicalCallContext entière en dynamique et envoyer tous les appels à côté de la dernière opération tracée.

Était-ce utile?

La solution

Ok, je me répondre.

courte. il n'y a pas de solution

Un peu détaillé:

Le problème est, je besoin d'un moyen de stocker la dernière opération active par chaque contexte logique. Code Tracing will've aucun contrôle sur le flux d'exécution, il est donc impossible de passer lastStartedOperation comme paramètre. contexte d'appel peut cloner (par exemple si un autre thread a commencé), alors je dois cloner la valeur en tant que clones de contexte.

CallContext.LogicalSetData () va bien, mais il fusionne des valeurs dans le contexte d'origine comme opération asynchrone terminée (en effet, en remplaçant toutes les modifications apportées avant EndInvoke appelé). Theortically, il peut occure même de manière asynchrone, ce qui donne résultat imprévisible de CallContext.LogicalGetData ().

Je dis théoriquement parce que simple appel a.EndInvoke () à l'intérieur d'un AsyncCallback ne remplace pas les valeurs dans le contexte d'origine. Bien que, je n'ai pas vérifié le comportement des appels d'accès distant (et il semble, WCF ne respecte pas du tout CallContext). En outre, la documentation (ancien) dit:

  

La méthode BeginInvoke passe le   CallContext au serveur. Quand   méthode EndInvoke est appelée, la   CallContext est fusionnée sur la   fil. Cela inclut les cas où   BeginInvoke et EndInvoke sont appelés   séquentiellement et où est BeginInvoke   appelé un fil et est EndInvoke   a appelé une fonction de rappel.

La dernière version est pas défini:

  

La méthode BeginInvoke passe le   CallContext au serveur. Quand le   Procédé EndInvoke est appelé, les données   contenue dans le CallContext est copié   en arrière sur le fil qui appelle   BeginInvoke.

Si vous digg dans la source-cadre, vous trouverez que les valeurs sont effectivement stockées dans une table de hachage à l'intérieur LogicalCallContext à l'intérieur ExecutionContext actuelle du thread courant.

Lorsque des clones de contexte d'appel (par exemple sur BeginInvoke) LogicalCallContext.Clone appelé. Et EndInvoke (au moins lorsqu'il est appelé à l'intérieur CallContext d'origine) appelle LogicalCallContext.Merge () remplacer les anciennes valeurs à l'intérieur m_Datastore par de nouvelles.

Nous avons donc besoin en quelque sorte fournir la valeur qui sera cloné mais pas refusionnés.

LogicalCallContext.Clone () également clones (sans fusion) le contenu de deux champs privés, m_RemotingData et m_SecurityData. Comme les types de champs définis comme internes, on ne pouvait pas tirer d'eux (même avec Emit), ajouter la propriété MyNoFlowbackValue et remplacez la valeur du champ m_RemotingData (ou d'un autre) avec instance de classe dérivée.

En outre, les types de terrain ne sont pas dérivés du MBR, il est donc impossible de les envelopper en utilisant proxy transparent.

Vous ne pouvait pas hériter de LogicalCallContext - il est scellé. (N.B. en fait, vous pourriez -.. Si vous utilisez le profilage CLR api pour remplacer IL comme cadres fantaisie font pas une solution souhaitée)

Vous ne pouvez remplacer la valeur m_Datastore, car LogicalCallContext sérialise uniquement le contenu de la table de hachage, pas le Hashtable lui-même.

Dernière solution est d'utiliser CallContext.HostContext. Ce efficacement stocke les données dans le domaine m_hostContext du LogicalCallContext. actions LogicalCallContext.Clone () (non clones) la valeur de m_hostContext, de sorte que la valeur doit être immuable. Pas un problème cependant.

Et même si cela échoue HttpContext utilisé, car il définit la propriété CallContext.HostContext remplacer votre ancienne valeur. Ironie du sort, HttpContext ne met pas ILogicalThreadAffinative, et n'a donc pas stocké en tant que valeur du champ m_hostContext. Il remplace juste valeur ancienne avec null.

Alors, il n'y a pas de solution et ne sera jamais, comme CallContext est la partie de l'accès distant et accès distant est obsolète.

P.S. Thace.CorrelationManager utilise CallContext ne interne et donc fonctionne pas comme vous le souhaitez, aussi. BTW, LogicalCallContext a solution spéciale à la pile d'exploitation de clone CorrelationManager sur le clone de contexte. Malheureusement, il n'a pas solution de contournement spéciale sur la fusion. Parfait!

P.P.S. L'exemple:

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();
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top