Ok, je me réponds moi-même.
Courte: il n'y a pas de solution.
détaillées légèrement:
Le problème est, il me faut un moyen de stocker la dernière opération active par chaque contexte logique. Le code de suivi n'aura aucun contrôle sur le flux d'exécution, il est donc impossible de passer lastStartedOperation en tant que paramètre. Le contexte d'appel peut cloner (par exemple, si un autre thread a démarré), j'ai donc besoin de cloner la valeur en tant que clones de contexte. CallContext.LogicalSetData() convient bien, mais il fusionne les valeurs dans le contexte d'origine lorsque l'opération asynchrone est terminée (en effet, en remplaçant toutes les modifications effectuées avant l'appel de EndInvoke). Theortically, il peut se produire même de manière asynchrone, donnant le résultat imprévisible de CallContext.LogicalGetData().
Je dis théoriquement parce que l'appel simple a.EndInvoke() à l'intérieur d'un asyncCallback ne remplace pas les valeurs dans le contexte d'origine. Cependant, je n'ai pas vérifié le comportement des appels à distance (et il semble que WCF n'honore pas du tout CallContext). En outre, le documentation (ancien) dit:
La méthode BeginInvoke passe le CallContext au serveur. Lorsque la méthode EndInvoke est appelée, le CallContext est fusionné sur l'unité d'exécution . Cela inclut les cas dans lesquels BeginInvoke et EndInvoke sont appelés séquentiellement et où BeginInvoke est appelé sur un thread et EndInvoke est appelé sur une fonction de rappel.
La dernière version est pas définie:
La méthode BeginInvoke passe le CallContext au serveur. Lorsque la méthode EndInvoke est appelée, les données contenues dans le CallContext est copié sur le thread qui a appelé 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.
Lors de l'appel de contextes clones (par exemple, sur BeginInvoke) appelé LogicalCallContext.Clone. Et EndInvoke (au moins lorsqu'il est appelé à l'intérieur de CallContext d'origine) appelle LogicalCallContext.Merge() en remplaçant les anciennes valeurs dans m_Datastore par de nouvelles.
Nous avons donc besoin de fournir la valeur qui sera clonée mais pas fusionnée. LogicalCallContext.Clone() clone également (sans fusionner) le contenu de deux champs privés, m_RemotingData et m_SecurityData. Comme les types du champ sont définis comme internes, vous ne pouvez pas en dériver (même avec emit), ajouter la propriété MyNoFlowbackValue et remplacer la valeur du champ m_RemotingData (ou un autre) par l'instance de la classe dérivée.
De même, les types de champs ne sont pas dérivés du MBR, il est donc impossible de les envelopper à l'aide d'un proxy transparent.
Vous ne pouviez pas hériter de LogicalCallContext - il est scellé. (NB en fait, vous pourriez - si vous utilisez l'api de profilage CLR pour remplacer IL comme le font les frameworks fictifs.)
Vous ne pouvez pas remplacer la valeur de m_Datastore, car LogicalCallContext ne sérialise que le contenu de la hashtable, pas la hashtable. lui-même.
La dernière solution consiste à utiliser CallContext.HostContext. Cela stocke efficacement les données dans le champ m_hostContext du LogicalCallContext. LogicalCallContext.Clone() partage (pas clone) la valeur de m_hostContext, donc la valeur doit être immuable. Pas un problème cependant.
Et même cela échoue si HttpContext est utilisé, car il définit la propriété CallContext.HostContext en remplaçant votre ancienne valeur. Ironiquement, HttpContext n'implémente pas ILogicalThreadAffinative, et par conséquent n'est pas stocké en tant que valeur du champ m_hostContext. Il remplace simplement l'ancienne valeur par null. Donc, il n'y a pas de solution et ne le sera jamais, car CallContext est la partie de l'accès distant et distant est obsolète.
P.S. Thace.CorrelationManager utilise CallContext en interne et ne fonctionne donc pas comme vous le souhaitez. BTW, LogicalCallContext a une solution de contournement spéciale pour cloner la pile d'opérations de CorrelationManager sur le clone de contexte. Malheureusement, il n'a pas de solution de rechange spéciale sur la fusion. Parfait!
P.P.S. L'échantillon:
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();
}
Merci pour l'écriture très détaillée, des informations très utiles pour moi en ce moment, et je reconnais qu'il est difficile à trouver! Merci de l'avoir posté –