62 votes

Durée de vie des applications AppDomain et MarshalByRefObject: comment éviter une RemotingException?

Lorsqu'un MarshalByRef objet est passé à partir d'un domaine d'application (1) à un autre (2), si vous attendez 6 minutes avant d'appeler une méthode dans le deuxième domaine d'application (2), vous obtiendrez un RemotingException :

Système.Moment de l'exécution.L'accès distant.RemotingException: L'objet [...] a été déconnecté ou n'existe pas sur le serveur.

Un peu de documentation sur ce isse :

Corrigez-moi si je me trompe : si InitializeLifetimeService renvoie la valeur nulle, l'objet ne peut être recueilli dans le domaine d'application 1 lorsque le domaine d'application 2 est Déchargé, même si la procuration a été recueillie ?

Est-il un moyen de le désactiver le temps de la vie et de garder le proxy (dans le domaine d'application 2) et l'objet (en AppDomain1) en vie jusqu'à ce que le proxy est Finalisé ? Peut-être avec ISponsor... ?

46voto

woohoo Points 1145

voir la réponse ici:

http://social.msdn.microsoft.com/Forums/en-US/netfxremoting/thread/3ab17b40-546f-4373-8c08-f0f072d818c9/

qui dit essentiellement:

 [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.Infrastructure)]
public override object InitializeLifetimeService()
{
  return null;
}
 

13voto

Guillaume Points 5649

J'ai enfin trouvé un moyen de faire client activé, les instances, mais il implique du code managé dans Finaliseur :( Je me suis spécialisée ma classe pour CrossAppDomain la communication, mais vous pouvez le modifier et à essayer dans les autres à distance. Laissez-moi savoir si vous trouvez un bug.

Les deux classes suivantes doivent être dans une assemblée chargée dans tous les domaines d'application concernés.

  /// <summary>
  /// Stores all relevant information required to generate a proxy in order to communicate with a remote object.
  /// Disconnects the remote object (server) when finalized on local host (client).
  /// </summary>
  [Serializable]
  [EditorBrowsable(EditorBrowsableState.Never)]
  public sealed class CrossAppDomainObjRef : ObjRef
  {
    /// <summary>
    /// Initializes a new instance of the CrossAppDomainObjRef class to
    /// reference a specified CrossAppDomainObject of a specified System.Type.
    /// </summary>
    /// <param name="instance">The object that the new System.Runtime.Remoting.ObjRef instance will reference.</param>
    /// <param name="requestedType"></param>
    public CrossAppDomainObjRef(CrossAppDomainObject instance, Type requestedType)
      : base(instance, requestedType)
    {
      //Proxy created locally (not remoted), the finalizer is meaningless.
      GC.SuppressFinalize(this);
    }

    /// <summary>
    /// Initializes a new instance of the System.Runtime.Remoting.ObjRef class from
    /// serialized data.
    /// </summary>
    /// <param name="info">The object that holds the serialized object data.</param>
    /// <param name="context">The contextual information about the source or destination of the exception.</param>
    private CrossAppDomainObjRef(SerializationInfo info, StreamingContext context)
      : base(info, context)
    {
      Debug.Assert(context.State == StreamingContextStates.CrossAppDomain);
      Debug.Assert(IsFromThisProcess());
      Debug.Assert(IsFromThisAppDomain() == false);
      //Increment ref counter
      CrossAppDomainObject remoteObject = (CrossAppDomainObject)GetRealObject(new StreamingContext(StreamingContextStates.CrossAppDomain));
      remoteObject.AppDomainConnect();
    }

    /// <summary>
    /// Disconnects the remote object.
    /// </summary>
    ~CrossAppDomainObjRef()
    {
      Debug.Assert(IsFromThisProcess());
      Debug.Assert(IsFromThisAppDomain() == false);
      //Decrement ref counter
      CrossAppDomainObject remoteObject = (CrossAppDomainObject)GetRealObject(new StreamingContext(StreamingContextStates.CrossAppDomain));
      remoteObject.AppDomainDisconnect();
    }

    /// <summary>
    /// Populates a specified System.Runtime.Serialization.SerializationInfo with
    /// the data needed to serialize the current System.Runtime.Remoting.ObjRef instance.
    /// </summary>
    /// <param name="info">The System.Runtime.Serialization.SerializationInfo to populate with data.</param>
    /// <param name="context">The contextual information about the source or destination of the serialization.</param>
    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
      Debug.Assert(context.State == StreamingContextStates.CrossAppDomain);
      base.GetObjectData(info, context);
      info.SetType(typeof(CrossAppDomainObjRef));
    }
  }

Et maintenant, le CrossAppDomainObject, votre distants objet doit hériter de cette classe au lieu de MarshalByRefObject.

  /// <summary>
  /// Enables access to objects across application domain boundaries.
  /// Contrary to MarshalByRefObject, the lifetime is managed by the client.
  /// </summary>
  public abstract class CrossAppDomainObject : MarshalByRefObject
  {
    /// <summary>
    /// Count of remote references to this object.
    /// </summary>
    [NonSerialized]
    private int refCount;

    /// <summary>
    /// Creates an object that contains all the relevant information required to
    /// generate a proxy used to communicate with a remote object.
    /// </summary>
    /// <param name="requestedType">The System.Type of the object that the new System.Runtime.Remoting.ObjRef will reference.</param>
    /// <returns>Information required to generate a proxy.</returns>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed override ObjRef CreateObjRef(Type requestedType)
    {
      CrossAppDomainObjRef objRef = new CrossAppDomainObjRef(this, requestedType);
      return objRef;
    }

    /// <summary>
    /// Disables LifeTime service : object has an infinite life time until it's Disconnected.
    /// </summary>
    /// <returns>null.</returns>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed override object InitializeLifetimeService()
    {
      return null;
    }

    /// <summary>
    /// Connect a proxy to the object.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public void AppDomainConnect()
    {
      int value = Interlocked.Increment(ref refCount);
      Debug.Assert(value > 0);
    }

    /// <summary>
    /// Disconnects a proxy from the object.
    /// When all proxy are disconnected, the object is disconnected from RemotingServices.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public void AppDomainDisconnect()
    {
      Debug.Assert(refCount > 0);
      if (Interlocked.Decrement(ref refCount) == 0)
        RemotingServices.Disconnect(this);
    }
  }

7voto

taffer Points 61

Malheureusement, cette solution est fausse lorsque les domaines d'application sont utilisés pour plugin fins (assemblée du plugin ne doit pas être chargé dans votre principal domaine d'application).

Le GetRealObject() appel à votre constructeur et le destructeur des résultats à obtenir le type réel de l'objet distant, ce qui conduit à essayer de charger l'assemblée de l'objet distant dans le domaine d'application. Cela peut entraîner soit une exception (si l'assemblée ne peut pas être chargé) ou de l'effet indésirable que vous avez chargé un montage à l'étranger que vous ne pouvez pas décharger plus tard.

Une meilleure solution peut être si vous vous inscrivez vos objets à distance dans votre principal domaine d'application avec ClientSponsor.Méthode Register () (pas statique, vous devez créer un client commanditaire exemple). Par défaut, il permettra de renouveler votre télécommande procurations à toutes les 2 minutes, ce qui est suffisant si vos objets a, par défaut, à 5 minutes de durée de vie.

0voto

Vous pourriez essayer un serializable singleton ISponsor objet la mise en œuvre de IObjectReference. Le GetRealObject de mise en œuvre (à partir de IObjectReference doit retourner MySponsor.Exemple lorsque le contexte.L'état est CrossAppDomain, sinon le retour lui-même. MySponsor.Instance est une auto-initialisation, synchronisée (MethodImplOptions.Synchronisé), singleton. Le Renouvellement de la mise en œuvre (à partir de ISponsor) doivent vérifier statique MySponsor.IsFlaggedForUnload et retour laps de Temps.Zéro lorsque marqué pour décharger/domaine d'application.Actuel.IsFinalizingForUnload() ou retour LifetimeServices.RenewOnCallTime autrement.

Pour le joindre, il suffit de demander un ILease et Registre(MySponsor.Exemple), qui sera transformé en MySponsor.Jeu d'instances dans le domaine d'application en raison de la GetRealObject mise en œuvre.

Pour arrêter le parrainage, le ré-obtenir le ILease et annuler l'inscription(MySponsor.Exemple), puis définissez le MySponsor.IsFlaggedForUnload par l'intermédiaire d'un cross-domaine d'application de rappel (myPluginAppDomain.DoCallback(MySponsor.FlagForUnload)).

Cela devrait garder votre objet vivant dans l'autre domaine d'application jusqu'à ce que le processus d'appel, le FlagForUnload appel, ou le domaine d'application du déchargement.

-2voto

DHornpout Points 1486

J'ai récemment rencontré cette exception aussi. Actuellement, ma solution consiste à décharger AppDomain, puis à recharger AppDomain après un long intervalle. Heureusement, cette solution temporaire fonctionne pour mon cas. Je souhaite qu'il y ait une manière plus élégante de traiter ceci.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X