2010-01-05 10 views
29

Je souhaite excéder un morceau de code dans un domaine AppDomain distinct avec un délégué. Comment puis-je faire ceci?Transférer et exécuter le délégué dans un AppDomain distinct

UPD1: quelques détails sur mon problème Mon traitement de programme des données (une itération est: obtenir des données de base de données, évaluer et créer des ensembles lors de l'exécution, l'exécution des ensembles dynamiques et écrire des résultats à DB).

Solution actuelle: chaque itération passe dans un filetage séparé. Meilleure solution: chaque itération s'exécutant dans AppDomain séparé (pour décharger les attaques dynamiques).

UPD2: Tous, merci pour les réponses.

J'ai trouvé un pour moi dans ce fil: Replacing Process.Start with AppDomains

Répondre

47

Bien que vous puissiez appeler un délégué qui sera géré par un AppDomain distinct, j'ai personnellement toujours utilisé la méthode 'CreateInstanceAndUnwrap' qui crée un objet dans le domaine de l'application étrangère et lui renvoie un proxy.

Pour que cela fonctionne votre objet doit hériter de MarshalByRefObject.

est un exemple:

public interface IRuntime 
    { 
     bool Run(RuntimesetupInfo setupInfo); 
    } 

    // The runtime class derives from MarshalByRefObject, so that a proxy can be returned 
    // across an AppDomain boundary. 
    public class Runtime : MarshalByRefObject, IRuntime 
    { 
     public bool Run(RuntimeSetupInfo setupInfo) 
     { 
      // your code here 
     } 
    } 

    // Sample code follows here to create the appdomain, set startup params 
    // for the appdomain, create an object in it, and execute a method 
    try 
    { 
     // Construct and initialize settings for a second AppDomain. 
     AppDomainSetup domainSetup = new AppDomainSetup() 
     { 
      ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase, 
      ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, 
      ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName, 
      LoaderOptimization = LoaderOptimization.MultiDomainHost 
     }; 

     // Create the child AppDomain used for the service tool at runtime. 
     childDomain = AppDomain.CreateDomain(
      "Your Child AppDomain", null, domainSetup); 

     // Create an instance of the runtime in the second AppDomain. 
     // A proxy to the object is returned. 
     IRuntime runtime = (IRuntime)childDomain.CreateInstanceAndUnwrap(
      typeof(Runtime).Assembly.FullName, typeof(Runtime).FullName); 

     // start the runtime. call will marshal into the child runtime appdomain 
     return runtime.Run(setupInfo); 
    } 
    finally 
    { 
     // runtime has exited, finish off by unloading the runtime appdomain 
     if(childDomain != null) AppDomain.Unload(childDomain); 
    } 

Dans l'exemple ci-dessus, il est codé pour exécuter un passage de la méthode « Exécuter » dans certaines informations de configuration, et l'achèvement de la méthode Run est déterminé à indiquer que tout le code dans l'enfant AppDomain a terminé son exécution, nous avons donc un bloc finally qui s'assure que l'AppDomain est déchargé.

Vous voudrez peut-être faire attention aux types que vous placez dans quels assemblages - vous pouvez utiliser une interface et la placer dans un assemblage séparé à la fois de l'appelant (notre code qui configure l'appdomain et des appels dans it) et l'implémenteur (la classe Runtime) dépendent de. Cet IIRC permet au parent AppDomain de charger uniquement l'assembly qui contient l'interface, tandis que l'appdomain enfant chargera à la fois l'assembly qui contient Runtime et sa dépendance (l'assembly IRuntime). Tous les types définis par l'utilisateur qui sont utilisés par l'interface IRuntime (par exemple, notre classe RuntimeSetupInfo) doivent généralement être placés dans le même assembly que IRuntime. Veillez également à définir ces types définis par l'utilisateur: s'il s'agit d'objets de transfert de données (comme RuntimeSetupInfo l'est probablement), vous devez probablement les marquer avec l'attribut [serializable] - afin qu'une copie de l'objet soit transmise (sérialisée l'appdomain parent à l'enfant). Vous voulez éviter que les appels soient regroupés d'un domaine à l'autre, car c'est plutôt lent. Passer des DTO par valeur (sérialisation) signifie que l'accès aux valeurs sur le DTO n'entraîne pas d'appel inter-appartement (puisque l'appdomain enfant a sa propre copie de l'original). Bien sûr, cela signifie également que les modifications de valeur ne sont pas répercutées dans le DTO d'origine du domaine d'application parent. Comme cela est codé dans l'exemple, le domaine d'application parent finira par charger les assemblys IRuntime et Runtime mais c'est parce que dans l'appel à CreateInstanceAndUnwrap j'utilise typeof (Runtime) pour obtenir le nom de l'assembly et le type complet prénom. Vous pouvez à la place coder en dur ou récupérer ces chaînes à partir d'un fichier - ce qui découplerait la dépendance.

Il existe également une méthode sur AppDomain nommée 'DoCallBack' qui semble permettre d'appeler un délégué dans un AppDomain étranger. Toutefois, le type de délégué qu'il prend est de type 'CrossAppDomainDelegate'. La définition de ce qui est:

public delegate void CrossAppDomainDelegate() 

Ainsi, il ne vous permettra pas de transmettre des données. Et, puisque je ne l'ai jamais utilisé, je ne peux pas vous dire s'il y a des pièges particuliers.

Aussi, je recommande de regarder dans la LoaderOptimization propriété. Ce que vous définissez, peut avoir un impact significatif sur les performances, car certains paramètres de cette propriété forcent le nouveau domaine à charger des copies séparées de tous les assemblages (et JIT, etc.) même si (IIRC) l'assembly est dans le GAC (c'est-à-dire que cela inclut les assemblages CLR). Cela peut vous donner des performances horribles si vous utilisez un grand nombre d'assemblys à partir de votre appdomain enfant. Par exemple, j'ai utilisé WPF à partir de domaines d'application enfants, ce qui a causé d'énormes retards de démarrage pour mon application jusqu'à ce que j'aie mis en place une politique de chargement plus appropriée.

+0

La méthode CreateInstanceAndUnwrap lance une erreur disant qu'il ne peut pas trouver l'assembly comme spécifié par (typeof (Runtime) .Assembly.FullName) . Cela fonctionne sur un serveur web pour moi et le format du nom complet est "myhostdll, Version = 1.0.0.0, Culture = neutre, PublicKeyToken = null". – Triynko

+0

@Triynko, lors de la création de la nouvelle configuration AppDomain, essayez de spécifier le même dossier bin que le domaine maître, ou du moins, utilisez un emplacement où le nouveau domaine peut obtenir les assemblys. –

3

Cela ne ne répondrait pas directement à votre question, mais peut-être préférable de créer un service WCF ou un service Web dans l'autre AppDomain pour préserver l'isolement. Je ne connais pas votre situation particulière, mais la conception architecturale isolée est presque toujours la bonne voie à suivre.

+3

En utilisant des tuyaux nommés, bien sûr. – Will

+0

En fait, je crée un tel service. Et je veux exécuter chaque demande dans un domaine séparé. –

6

Vous avez besoin de lire sur .NET Remoting et plus précisément sur Remote Objects car ce sont tous que vous pouvez passer par AppDomains.

La longue et courte, il est que votre objet est soit passé par la valeur ou par référence (via un proxy).

Par valeur nécessite que votre objet soit sérialisable. Les délégués ne sont pas sérialisables. Cela signifie que ce n'est pas une bonne voie à suivre. Par défaut, vous devez hériter de MarshalByRefObject. De cette façon, l'infrastructure d'accès distant peut créer le proxy. Toutefois, cela signifie également que votre délégué sera exécuté sur l'ordinateur qui le crée, et non sur le domaine de l'application cliente.

Dans l'ensemble, ça va être difficile. Vous voudrez peut-être envisager de faire de vos délégués des objets sérialisables à part entière afin qu'ils puissent être facilement déplacés à distance (et fonctionneront bien avec d'autres technologies).

+0

Etes-vous sûr de pouvoir utiliser un délégué? De ce que je peux voir, comme mentionné dans mon article, vous pouvez seulement utiliser un délégué de type 'CrossAppDomainDelegate' – Phil

Questions connexes