“对象已断开连接或在服务器上不存在”异常

时间:2011-06-14 05:31:07

标签: c# .net clr appdomain marshalbyrefobject

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

  

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

目标对象仍然存在,我已经检查过了。

UPD我在目标对象的终结器中设置断点,它永远不会命中。因此,这个对象是活着的,而不是GC。

7 个答案:

答案 0 :(得分:33)

这可能是因为服务器端的本地垃圾收集器收集了该对象。您可以通过续订租赁来防止这种情况。您可以在这些文章中阅读更多相关内容:

更新:不幸的是,MSDN杂志2008年或更早版本的问题不再可以在线浏览,而只能作为必须下载到本地计算机的.chm文件。以前的问题可以在以下网站找到:

答案 1 :(得分:15)

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

答案 2 :(得分:2)

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

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

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

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

    /// <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,允许它调用插件,即使它忽略了它们的真实类型。因此,可以从主AppDomain访问的PluginProxy必须序列化以跨越AppDomains限制。我遇到了问题,因为我没有赞助这些MarshalByRefObject:

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

    要解决这个问题很困难,希望这会有所帮助!

    参考文献:

答案 3 :(得分:1)

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

public override object InitializeLifetimeService(){
    return null;
}

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

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

此问题还有助于弄清有关寿命的一些问题:stackoverflow-question

答案 4 :(得分:1)

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

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

在我的情况下,这发生在存储在App_Data项目内的Web文件夹中的SQL LocalDB中。每当我尝试使用Package Console运行update-database来使用迁移来初始化我的Entity Framework数据库时,都不会发生任何事情。过了一会儿,我得到了那个错误。

我通过修改App_Data上的文件权限解决了这个问题。一旦修好,瞧,它有效。