“Object has been disconnected or does not exist at the server” exception

后端 未结 7 854
孤街浪徒
孤街浪徒 2020-12-04 19:31

I need to use cross-appdomain calls in my app, and sometimes I have this RemotingException:

Object \'/2fa53226_da41_42ba_b185_ec7d9c454712/ygiw+xfegmk

7条回答
  •  醉话见心
    2020-12-04 19:56

    I had the same problem and I searched for many hours with help of many StackOverflow posts.

    I finally found the complete issue.

    1. I have to use a Sponsor to maintain my MarshalByRefObject alive.
    2. I then had the same problem than @user626528 : object is alive but I had the exception. In fact, I needed to "sponsor" ALL THE "TransparentProxy" instances, and not only the main one : my main Object created in SandBox (another AppDomain) returns references to other MarshalByRefObjects.

    Here is the complete explanation and use case :

    My class "Loader" inherits from MarshalByRefObject, and I keep it alive with a ISponsor class. I know "ClientSponsor" exists in .NET, but I had no way to determine if and when Renewal() is called, so I made my class with help of StackOverflow community (read code comments) :

    /// 
    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);
        }
    }
    

    And then I used this "custom sponsor" like this:

    // 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.
            }
        }
    
     }
    

    The PluginProxy type has a reference towards the real plugin type. Indeed, the PluginProxy is instanciated inside Plugin AppDomain, and returned to main AppDomain, to allow it to call Plugins even if it ignore their real type. So the PluginProxy, to be accessible from main AppDomain, have to be serialized to cross AppDomains limits. I had a problem because I didn't sponsored these MarshalByRefObject(s) :

     /// 
        [Serializable]
        public class PluginProxy : MarshalByRefObject, ICustomPlugin
        {
            private ICustomPlugin _hostedPlugin;            
    
            /// 
            /// Parameterless constructor for deserialization 
            /// 
            public PluginProxy()
            {             
            }
    
            ~PluginProxy()
            {
                Debug.WriteLine("DESTRUCTOR ~PluginProxy");
            }
    
            /// 
            /// Constructor reserved from real Plugin type
            /// 
            /// 
            public PluginProxy(ICustomPlugin hostedPlugin)
            {
                _hostedPlugin = hostedPlugin;
            }
    
            public PluginName Name => _hostedPlugin.Name;
    
            public PluginResult Execute(PluginParameters parameters, PluginQuery query)
            {
                return(_hostedPlugin.Execute(parameters, query));
            }
        }
    

    It was a difficult bunch of problems to solve, hope this helps !

    References:

    • MSDN: ILease interface
    • MSDN: ObjRef class
    • Microsoft: How to marshal an object to a remote server by reference by using Visual C#
    • StackOverflow: Remoting sponsor stops being called

    • StackOverflow: How do the ISponsor and ILease interfaces work?

    • StackOverflow: How to pass an unknown type between two .NET AppDomains?
    • StackOverflow: AppDomain and MarshalByRefObject life time : how to avoid RemotingException?
    • StackOverflow : MarshalByRefObject becoming “disconnected at server” even while sponsored

提交回复
热议问题