【问题标题】:"Object has been disconnected or does not exist at the server" exception“对象已断开连接或在服务器上不存在”异常
【发布时间】:2011-09-14 10:21:40
【问题描述】:

我需要在我的应用程序中使用跨应用程序域调用,有时我有这个 RemotingException:

对象“/2fa53226_da41_42ba_b185_ec7d9c454712/ygiw+xfegmkhdinj7g2kpkhc_7.rem”已断开连接或在服务器上不存在。

目标对象还活着,我已经检查过了。

UPD 我在目标对象的终结器中设置了断点,但它永远不会命中。因此,这个对象是存活的并且没有被 GC 处理。

【问题讨论】:

标签: c# .net clr appdomain marshalbyrefobject


【解决方案1】:

这可能是因为服务器端的本地垃圾收集器收集了对象。您可以通过更新租约来防止这种情况发生。您可以在以下文章中了解更多相关信息:

更新:很遗憾,2008 年或更早的 MSDN 杂志问题不再可在线浏览,只能以 .chm 文件的形式下载到本地计算机。以前的问题可以在:

【讨论】:

  • 如果您禁用了生命周期管理,该对象仍然通过远程处理暴露(因此它不能被 GC'ed),直到您在服务器端断开它。
  • Server端你可以调用RemotingServices.Disconnect(myObject);
  • Remoting 保持对对象的引用,只要它被暴露。如果生命周期是无限的,Remoting 将永远保留引用,除非您明确告诉它关闭服务器并释放引用(通过调用 Disconnect 完成)。
  • @user626528 引用已死?什么时候 ?你的意思是不改变寿命?如果是这样,那是因为如果客户端在一段时间内没有调用任何方法,默认Remoting会自动断开它并释放它的引用。
  • @MattBurland CHM 文件对我有用,但只有在我将它们作为website says 取消阻止后才能使用。右键单击 .chm 文件,转到属性,然后单击“取消阻止”。请注意,在我这样做之前,文件会打开并显示内容,但不会呈现任何页面。解锁后,它显示整个文件就好了。
【解决方案2】:

这是因为服务器端的 Lifetime 管理在对象的租约到期时断开连接,以允许 GC 收集它。如果您尝试从客户端使用它,您将得到一个异常,即使它尚未在服务器上进行 GC(例如,因为仍然有另一个引用)但租约已过期。这是为了避免不可预测的行为。接受的答案为如何正确管理远程 .NET 对象的生命周期提供了很好的参考。

【讨论】:

    【解决方案3】:

    我遇到了同样的问题,我在许多 StackOverflow 帖子的帮助下搜索了好几个小时。

    我终于找到了完整的问题。

    1. 我必须使用 Sponsor 来维持我的 MarshalByRefObject 活着。
    2. 然后我遇到了与@user626528 相同的问题:对象还活着,但我遇到了异常。事实上,我需要“赞助”所有“TransparentProxy”实例,而不仅仅是主要的:我在 SandBox(另一个 AppDomain)中创建的主要对象返回对其他 MarshalByRefObjects 的引用。李>

    这是完整的解释和用例:

    我的“Loader”类继承自 MarshalByRefObject,我通过 ISponsor 类保持它的活力。我知道 .NET 中存在“ClientSponsor”,但我无法确定是否以及何时调用 Renewal(),所以我在 StackOverflow 社区的帮助下创建了我的课程(阅读代码 cmets):

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

    然后我像这样使用这个“自定义赞助商”:

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

    PluginProxy 类型具有对真实插件类型的引用。实际上,PluginProxy 在 Plugin AppDomain 中实例化,并返回到主 AppDomain,以允许它调用 Plugins,即使它忽略了它们的真实类型。因此,可以从主 AppDomain 访问的 PluginProxy 必须被序列化以跨越 AppDomains 限制。我遇到了问题,因为我没有赞助这些 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));
            }
        }
    

    这是一堆难以解决的问题,希望对您有所帮助!

    参考资料:

    【解决方案4】:

    这发生在我们身上,因为我们的一个类中有一个 AppDomain 类型的静态变量。该类用于长期运行的 Windows 服务。 AppDomain 有一个 InitializeLifetimeService 方法,需要像这样重写:

    public override object InitializeLifetimeService(){
        return null;
    }
    

    我们一直使用它作为私有变量,为自定义构建的外部逻辑加载和卸载一些 dll。 答案取自这里:msdn answer

    因为我们无法在生产时更改此设置,我们以一种折衷的方式结束,即以比静态 AppDomain 变量的生命周期更短的随机间隔重新启动 Windows 服务,通过反复试验我们发现它是几个天。

    这个问题也有助于澄清一些关于生命周期的事情:stackoverflow-question

    【讨论】:

      【解决方案5】:

      这个问题是answered in great detail already on StackOverflow。 TL/DR:

      1. 如果你想要单例语义覆盖InitializeLifetimeService to return null
      2. 使用ClientSponsor 让您的对象存活更长时间。

      【讨论】:

      • 其中一个未记录的技巧是赞助商也是一个远程对象。 ClientSponsor 初始化自身,覆盖 InitializeLifetimeService 以返回 null,因此 it 不会过期并停止响应续订请求。如果赞助商过期,远程对象最终也会过期(如果不使用单例语义)。
      【解决方案6】:

      在我的情况下,问题是在客户端计算机中,有一个虚拟网络适配器处于活动状态,禁用虚拟网络适配器,问题就解决了

      【讨论】:

        【解决方案7】:

        在我的例子中,这发生在存储在 Web 项目内的 App_Data 文件夹中的 SQL LocalDB 上。每当我尝试使用包控制台运行update-database 以使用迁移来初始化我的实体框架数据库时,什么都没有发生。然后过了一会儿,我得到了那个错误。

        我通过修改App_Data 的文件权限解决了这个问题。修复后,瞧,它起作用了。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-10-24
          • 2013-05-31
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多