Comment puis-je passer des références en tant que paramètres de la méthode à travers AppDomains?

StackOverflow https://stackoverflow.com/questions/2932370

Question

J'ai essayé d'obtenir le code suivant au travail (tout est défini dans le même ensemble):

namespace SomeApp{

public class A : MarshalByRefObject
{
   public byte[] GetSomeData() { // }
}

public class B : MarshalByRefObject
{
   private A remoteObj;

   public void SetA(A remoteObj)
   {
      this.remoteObj = remoteObj;
   }
}

public class C
{
   A someA = new A();
   public void Init()
   {
       AppDomain domain = AppDomain.CreateDomain("ChildDomain");
       string currentAssemblyPath = Assembly.GetExecutingAssembly().Location;
       B remoteB = domain.domain.CreateInstanceFromAndUnwrap(currentAssemblyPath,"SomeApp.B") as B;
       remoteB.SetA(someA); // this throws an ArgumentException "Object type cannot be converted to target type."
   }
}

}

Ce que je suis en train de faire est de passer une référence d'une instance « A » créé dans le premier AppDomain au domaine des enfants et avoir le domaine enfant exécuter une méthode sur le premier domaine. Dans un point sur le code « B » Je vais appeler « remoteObj.GetSomeData () ». Cela doit être fait parce que la méthode « octet [] » de « GetSomeData » doit être « calculée » sur la première appdomain.   Que dois-je faire pour éviter l'exception, ou que puis-je faire pour obtenir le même résultat?

Était-ce utile?

La solution

Je peux reproduire le problème, et il semble être lié à TestDriven.net et / ou xUnit.net. Si je lance C.Init () comme méthode d'essai, je reçois le même message d'erreur. Cependant, si je lance C.Init () à partir d'une application console, je ne reçois pas l'exception.

Voyez-vous la même chose, en cours d'exécution C.Init () à partir d'un test unitaire?

Edit: Je suis également en mesure de reproduire le problème en utilisant NUnit et TestDriven.net. Je suis également en mesure de reproduire l'erreur en utilisant le coureur NUnit au lieu de TestDriven.net. Le problème semble être lié à l'exécution de ce code dans un cadre de test, mais je ne sais pas pourquoi.

Autres conseils

La cause réelle a été votre dll a été chargé de se à différents endroits dans les deux domaines d'applications différentes. Cela provoque .NET à penser qu'ils sont différents assemblages de moyens de cours sont les types différents (même si elles ont le même nom de classe, espace de noms, etc.).

La raison pour laquelle le test de Jeff échoué lorsqu'il est exécuté par un cadre de test unitaire est parce que les cadres de tests unitaires créent généralement AppDomains avec jeu ShadowCopy à « true ». Mais votre AppDomain créé manuellement serait par défaut ShadowCopy = « false ». Cela causerait les dll à charger à partir de différents endroits qui mène à la belle « type d'objet ne peuvent pas être convertis en type de cible. » erreur.

MISE À JOUR: Après d'autres essais, il ne semble descendre à l'ApplicationBase étant différent entre les deux domaines d'application. Si elles correspondent, les travaux de scénario ci-dessus. Si elles sont différentes, il ne (même si je l'ai confirmé que le dll est chargé dans les deux domaines d'application à partir du même répertoire à l'aide windbg) De plus, si je retourne sur ShadowCopy = « true » dans mes deux domaines d'application, il ne avec un autre message: "System.InvalidCastException: L'objet doit mettre en œuvre IConvertible"

MAJ2: conduit de lecture D'autres me croire qu'il est lié à Contextes charge . Lorsque vous utilisez l'un des « From » (méthodes Assembly.LoadFrom ou appDomain.CreateInstanceFromAndUnwrap), si l'assemblage se trouve dans l'un des chemins de charge normale (ou la ApplicationBase l'un des chemins de sondage) est-il alors chargé dans le défaut Contexte de charge. Si l'est assemblée ne s'y trouve pas, alors il est chargé dans la charge-le contexte. Ainsi, lorsque les deux domaines d'application de contrepartie de ApplicationBase de, alors même si nous utilisons un « From » méthode, ils sont à la fois chargés dans leur contexte par défaut de charge de AppDomain respective. Mais quand les années de ApplicationBase sont différents, l'un AppDomain aura l'ensemble dans son contexte de chargement par défaut tandis que l'autre a l'ensemble dans ce Chargeons-à partir du contexte.

Ceci est un commentaire à @RussellMcClure mais comme il est complexe pour un commentaire Je poste ceci comme une réponse:

Je suis dans une application ASP.NET et éteindre cliché instantané (qui résoudrait aussi le problème) n'est pas vraiment une option, mais j'ai trouvé la solution suivante:

AppDomainSetup adSetup = new AppDomainSetup();
if (AppDomain.CurrentDomain.SetupInformation.ShadowCopyFiles == "true")
{
    var shadowCopyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    if (shadowCopyDir.Contains("assembly"))
        shadowCopyDir = shadowCopyDir.Substring(0, shadowCopyDir.LastIndexOf("assembly"));

    var privatePaths = new List<string>();
    foreach (var dll in Directory.GetFiles(AppDomain.CurrentDomain.SetupInformation.PrivateBinPath, "*.dll"))
    {
        var shadowPath = Directory.GetFiles(shadowCopyDir, Path.GetFileName(dll), SearchOption.AllDirectories).FirstOrDefault();
        if (!String.IsNullOrWhiteSpace(shadowPath))
            privatePaths.Add(Path.GetDirectoryName(shadowPath));
    }

    adSetup.ApplicationBase = shadowCopyDir;
    adSetup.PrivateBinPath = String.Join(";", privatePaths);
}
else
{
    adSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
    adSetup.PrivateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;
}

utilisera le répertoire shadow-copie de la principale application domaine comme la base de l'application et ajouter tous les ensembles copié ombre à l'chemin privé si l'ombre copie est activée.

Si quelqu'un a une meilleure façon de le faire s'il vous plaît me dire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top