web-dev-qa-db-fra.com

Exception "L'objet a été déconnecté ou n'existe pas sur le serveur"

Je dois utiliser des appels inter-applications dans mon application et j'ai parfois cette exception RemotingException:

L'objet '/2fa53226_da41_42ba_b185_ec7d9c454712/ygiw+xfegmkhdinj7g2kpkhc_7.rem' a été déconnecté ou n'existe pas sur le serveur. 

L'objet cible est toujours en vie, je l'ai vérifié.

UPD J'ai défini un point d'arrêt dans le finaliseur de l'objet cible et il ne touche jamais. Ainsi, cet objet est en vie et n'a pas été GC'ed.

41
user626528

Cela est probablement dû au fait que le ramasse-miettes local côté serveur collecte l'objet. Vous pouvez empêcher cela en renouvelant le crédit-bail. Vous pouvez en savoir plus à ce sujet dans ces articles:

Update: Malheureusement, les numéros de MSDN Magazine de 2008 ou les versions antérieures ne sont plus consultables en ligne, mais uniquement sous forme de fichiers .chm que vous devez télécharger sur votre ordinateur local. Les numéros précédents peuvent être trouvés dans:

33
Marius Bancila

En effet, la gestion de la durée de vie côté serveur déconnecte l'objet lorsque son bail expire, afin de permettre au GC de le collecter. Si vous essayez de l’utiliser du côté client, vous obtiendrez une exception, même si elle n’a pas encore été attribuée par GC sur le serveur (par exemple, car il ya encore une autre référence) mais le bail a expiré. Ceci afin d'éviter un comportement imprévisible. La réponse acceptée fournit une bonne référence sur la façon de gérer correctement la durée de vie des objets .NET distants.

15
Stefan Paul Noack

J'ai eu le même problème et j'ai cherché de nombreuses heures à l'aide de nombreux messages StackOverflow.

J'ai enfin trouvé le numéro complet.

  1. Je dois utiliser un sponsor pour maintenir mon MarshalByRefObject en vie.
  2. J'avais alors le même problème que @ user626528: l'objet est en vie mais j'avais l'exception. En fait, j'avais besoin de "parrainer" TOUTES les " TransparentProxy " instances, et pas seulement le principal: mon objet principal créé dans SandBox (un autre AppDomain) renvoie des références à d'autres MarshalByRefObjects.

Voici l'explication complète et le cas d'utilisation:

Ma classe "Loader" hérite de MarshalByRefObject et je la garde en vie avec une classe ISponsor. Je sais que "ClientSponsor" existe dans .NET, mais je n'avais aucun moyen de déterminer si et quand Renewal () était appelé. J'ai donc créé ma classe à l'aide de la communauté StackOverflow (lire les commentaires de code):

/// <see cref="https://stackoverflow.com/questions/18680664/remoting-sponsor-stops-being-called"/>
public class RemotingSponsor : MarshalByRefObject, ISponsor, IDisposable
{
    /*
     * @CoryNelson said :
     * I've since determined that the ILease objects of my sponsors 
     * themselves are being GCed. They start out with the default 5min lease 
     * time, which explains how often my sponsors are being called. When I 
     * set my InitialLeaseTime to 1min, the ILease objects are continually        
     * renewed due to their RenewOnCallTime being the default of 2min.
     * 
     */ 

    ILease _lease;

    public RemotingSponsor(MarshalByRefObject mbro)
    {
        _lease = (ILease)RemotingServices.GetLifetimeService(mbro);
        if (_lease == null) throw new NotSupportedException("Lease instance for MarshalByRefObject is NULL");
        _lease.Register(this);
    }

    public TimeSpan Renewal(ILease lease)
    {
        Debug.WriteLine("RemotingSponsor.Renewal called");
        return this._lease != null ? lease.InitialLeaseTime : TimeSpan.Zero;
    }


    public void Dispose()
    {
        if (_lease != null)
        {
            _lease.Unregister(this);
            _lease = null;
        }
    }

    public override object InitializeLifetimeService()
    {
        /*
         *
         * @MatthewLee said:
         *   It's been a long time since this question was asked, but I ran into this today and after a couple hours, I figured it out. 
         * The 5 minutes issue is because your Sponsor which has to inherit from MarshalByRefObject also has an associated lease. 
         * It's created in your Client domain and your Host domain has a proxy to the reference in your Client domain. 
         * This expires after the default 5 minutes unless you override the InitializeLifetimeService() method in your Sponsor class or this sponsor has its own sponsor keeping it from expiring.
         *   Funnily enough, I overcame this by returning Null in the sponsor's InitializeLifetimeService() override to give it an infinite timespan lease, and I created my ISponsor implementation to remove that in a Host MBRO.
         * Source: https://stackoverflow.com/questions/18680664/remoting-sponsor-stops-being-called
        */
        return (null);
    }
}

Et puis j'ai utilisé ce "sponsor personnalisé" comme ceci:

// Loader and Container for MarshalByRefObject in another domain
 public class PluginFile : IDisposable
 {
           private RemotingSponsor _sponsor; // Keep instance not to have Sponsor Garbage Collected
           private AppDomain _sandbox;
           private ICustomPlugin[] _plugins; // I do not store real instances of Plugins, but a "CustomPluginProxy" which is known both by main AppDomain and Plugin AppDomain.

    // Constructor : load an Assembly file in another AppDomain (sandbox)
    public PluginFile(System.IO.FileInfo f, AppDomainSetup appDomainSetup, Evidence evidence)
    {
        Directory = System.IO.Path.GetDirectoryName(f.FullName) + @"\";
        _sandbox = AppDomain.CreateDomain("sandbox_" + Guid.NewGuid(), evidence, appDomainSetup);

        _sandbox.Load(typeof(Loader).Assembly.FullName);

        // - Instanciate class "Loader" INSIDE OTHER APPDOMAIN, so we couldn't use new() which would create in main AppDomain.
        _loader = (Loader)Activator.CreateInstance(
            _sandbox,
            typeof(Loader).Assembly.FullName,
            typeof(Loader).FullName,
            false,
            BindingFlags.Public | BindingFlags.Instance,
            null,
            null,
            null,
            null).Unwrap();

        // - Load plugins list for Assembly
        _plugins= _loader.LoadPlugins(f.FullName); 


        // - Keep object created in other AppDomain not to be "Garbage Collected". I create a sponsor. The sponsor in registed for object "Lease". The LeaseManager will check lease expiration, and call sponsor. Sponsor can decide to renew lease. I not renewed, the object is garbage collected.
        // - Here is an explanation. Source: https://stackoverflow.com/questions/12306497/how-do-the-isponsor-and-ilease-interfaces-work
        _sponsor = new RemotingSponsor(_loader);

       // Here is my SOLUTION after many hours ! I had to sponsor each MarshalByRefObject (plugins) and not only the main one that contains others !!!
       foreach (ICustomPlugin plugin in Plugins) 
        {
            ILease lease = (ILease)RemotingServices.GetLifetimeService((PluginProxy)plugin);
            lease.Register(_sponsor); // Use the same sponsor. Each Object lease could have as many sponsors as needed, and each sponsor could be registered in many Leases.
        }
    }

 }

Le type PluginProxy a une référence vers le type de plugin réel. En effet, PluginProxy est instancié dans Plugin AppDomain et renvoyé à AppDomain principal pour lui permettre d’appeler des plugins même s’il ignore leur type réel. Ainsi, PluginProxy, pour être accessible à partir d'AppDomain principal, doit être sérialisé pour dépasser les limites d'AppDomains. J'ai eu un problème parce que je n'ai pas sponsorisé ces MarshalByRefObject (s):

 /// <see cref="https://stackoverflow.com/questions/4185816/how-to-pass-an-unknown-type-between-two-net-appdomains"/>
    [Serializable]
    public class PluginProxy : MarshalByRefObject, ICustomPlugin
    {
        private ICustomPlugin _hostedPlugin;            

        /// <summary>
        /// Parameterless constructor for deserialization 
        /// </summary>
        public PluginProxy()
        {             
        }

        ~PluginProxy()
        {
            Debug.WriteLine("DESTRUCTOR ~PluginProxy");
        }

        /// <summary>
        /// Constructor reserved from real Plugin type
        /// </summary>
        /// <param name="name"></param>
        public PluginProxy(ICustomPlugin hostedPlugin)
        {
            _hostedPlugin = hostedPlugin;
        }

        public PluginName Name => _hostedPlugin.Name;

        public PluginResult Execute(PluginParameters parameters, PluginQuery query)
        {
            return(_hostedPlugin.Execute(parameters, query));
        }
    }

Ce fut un tas de problèmes difficiles à résoudre, espérons que cela aide!

Références:

2
Elo

Cela s'est produit pour nous car nous avions une variable statique dans l'une de nos classes qui était de type AppDomain. La classe a été utilisée dans un service Windows long en cours d'exécution . AppDomain a une méthode InitializeLifetimeService qui doit être remplacée de la manière suivante:

public override object InitializeLifetimeService(){
    return null;
}

Nous utilisions constamment cette variable comme variable privée qui chargeait et déchargeait des dll pour une logique construite en dehors de la logique . La réponse a été prise ici: msdn answer

Étant donné que nous n’avons pas été en mesure de le modifier au moment de la production, nous avons compromis le redémarrage du service Windows à des intervalles aléatoires inférieurs à la durée de vie de la variable statique AppDomain. Après plusieurs essais, nous avons constaté qu’il s’agissait de plusieurs jours.

Cette question a également permis de clarifier certaines choses concernant la vie: stackoverflow-question

1
Allen

dans mon cas, le problème était que dans l'ordinateur client, il y avait une carte réseau virtuelle active, désactivant les cartes réseau virtuelles, le problème a été résolu

0
Ismael

Cette question a été répondu de manière très détaillée déjà sur StackOverflow . TL/DR:

  1. Si vous voulez que la sémantique de Singleton écrase InitializeLifetimeService pour renvoyer null
  2. Utilisez ClientSponsor pour vous garder plus longtemps en vie. 
0
cdiggins

Dans mon cas, cela se produisait avec SQL LocalDB stocké dans le dossier App_Data du projet Web. Chaque fois que j'essaie d'utiliser la console de package pour exécuter update-database afin d'initialiser ma base de données Entity Framework à l'aide de migrations, rien ne se produit. Puis au bout d'un moment, j'obtiens cette erreur.

J'ai résolu ce problème en révisant les autorisations de fichier sur App_Data. Une fois réparé, le tour est joué, ça a fonctionné.

0
Korayem