web-dev-qa-db-ja.com

「オブジェクトが切断されたか、サーバーに存在しない」例外

アプリでクロスアプリドメインコールを使用する必要があり、このRemotingExceptionが発生することがあります。

オブジェクト '/2fa53226_da41_42ba_b185_ec7d9c454712/ygiw+xfegmkhdinj7g2kpkhc_7.rem'が切断されているか、サーバーに存在しません。

ターゲットオブジェクトはまだ生きています。チェックしました。

UPDターゲットオブジェクトのファイナライザにブレークポイントを設定しましたが、ヒットしません。したがって、このオブジェクトは有効で、GCされていません。

42
user626528

これはおそらく、サーバー側のローカルガベージコレクターがオブジェクトを収集するためです。リースを更新することでそれを防ぐことができます。あなたはこれらの記事でそれについてもっと読むことができます:

更新:残念ながら、2008年以前のMSDN Magazineの問題はオンラインで閲覧できなくなり、ローカルにダウンロードする必要がある.chmファイルとしてのみ閲覧できます機械。以前の問題は次の場所にあります。

  • 2003年12月号 のリースとスポンサーシップによるリモート.NETオブジェクトの寿命の管理
  • CLR Inside Out:Object Lifetime in 2007年11月号
33
Marius Bancila

これは、GCがオブジェクトを収集できるように、リースが期限切れになると、サーバー側のライフタイム管理がオブジェクトを切断するためです。クライアント側から使用しようとすると、まだサーバーでGCされていない場合でも(たとえば、まだ別の参照があるため)、リースの有効期限が切れていても、例外が発生します。これは、予測できない動作を回避するためです。受け入れられた回答は、リモート.NETオブジェクトの存続期間を正しく管理する方法についての適切なリファレンスを提供します。

15

私は同じ問題を抱えていて、多くのStackOverflow投稿の助けを借りて何時間も検索しました。

ようやく完全な問題が見つかりました。

  1. MarshalByRefObjectを有効に維持するには、スポンサーを使用する必要があります。
  2. 次に、@ user626528と同じ問題が発生しました。オブジェクトは生きていますが、例外がありました。実際、私は、すべての「 TransparentProxy "instancesを「スポンサー」する必要があり、メインのインスタンスだけでなく、私のメインSandBox(別のAppDomain)で作成されたオブジェクトは、他のMarshalByRefObjectへの参照を返します。

ここに完全な説明とユースケースがあります:

私のクラス「ローダー」はMarshalByRefObjectを継承しており、ISponsorクラスを使用してそれを維持しています。 「ClientSponsor」が.NETに存在することは知っていますが、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は、AppDomainの制限を超えるようにシリアル化する必要があります。これらの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));
        }
    }

解決するのが難しい問題の集まりでした。これが役に立てば幸いです!

参照:

3
Elo

これは、AppDomainタイプのクラスの1つに静的変数があったために発生しました。このクラスは、長時間実行されているWindowsサービスで使用されていました。 AppDomainには、次のようにオーバーライドする必要があるInitializeLifetimeServiceメソッドがあります。

public override object InitializeLifetimeService(){
    return null;
}

これを、カスタムビルドされた外部ロジック用のいくつかのdllをロードおよびアンロードするプライベート変数として常に使用していました。答えはここから取られました: msdn answer

運用時にこれを変更できなかったため、試行錯誤により数日であることが判明した静的AppDomain変数の有効期間よりも短いランダムな間隔でWindowsサービスを再起動する妥協案で終わりました。

この質問は、寿命に関するいくつかのことを明確にするのにも役立ちました: stackoverflow-question

1
Allen

私の場合、問題は、クライアントコンピューターでアクティブな仮想ネットワークアダプターがあり、仮想ネットワークアダプターが無効になっていて、問題が解決したことです

0
Ismael

この質問は StackOverflowで既に詳しく回答されています です。 TL/DR:

  1. シングルトンセマンティクスのオーバーライドが必要な場合 InitializeLifetimeServiceがnullを返す
  2. ClientSponsor を使用して、オブジェクトを長く存続させます。
0
cdiggins

私の場合、これはWebプロジェクト内のApp_Dataフォルダに保存されているSQL LocalDBで発生していました。パッケージコンソールを使用してupdate-databaseを実行し、移行を使用してEntity Frameworkデータベースを初期化しようとしても、何も起こりません。その後しばらくすると、そのエラーが発生します。

App_Dataのファイル権限を修正することでこれを解決しました。修正されたら、うまくいきました。

0
Korayem