「オブジェクトが切断されているか、サーバーに存在していない」例外
-
27-10-2019 - |
質問
アプリでクロスアプライドコールを使用する必要がありますが、時にはこのRemotingExceptionがあります。
Object '/2FA53226_DA41_42BA_B185_EC7D9C454712/YGIW+XFEGMKHDINJ7G2KPKHC_7.REM'が切断されているか、サーバーに存在しません。
ターゲットオブジェクトはまだ生きています、私はそれをチェックしました。
UPDターゲットオブジェクトのファイナルライザーでブレークポイントを設定しましたが、決してヒットしません。したがって、このオブジェクトは生きており、GC'edではありませんでした。
解決
それはおそらく、サーバー側のローカルゴミコレクターがオブジェクトを収集したためです。リースを更新することでそれを防ぐことができます。これらの記事で詳細を読むことができます。
アップデート: :残念ながら、2008年以降のMSDNマガジンの問題は、オンラインで閲覧可能ではなく、ローカルマシンにダウンロードする必要がある.CHMファイルとしてのみ閲覧できます。以前の問題は、次のように見つけることができます。
他のヒント
これは、サーバー側のライフタイム管理が、リースが期限切れになったときにオブジェクトを切断し、GCが収集できるためです。クライアント側からそれを使用しようとすると、サーバー上でまだGC'dがない場合でも(たとえば、まだ別の参照があるため)、リースの有効期限が切れている場合でも、例外が得られます。これは、予測不可能な行動を避けるためです。受け入れられた回答は、リモート.NETオブジェクトの寿命を正しく管理する方法に関する適切な参照を提供します。
私は同じ問題を抱えていたので、多くのStackOverFlowの投稿の助けを借りて何時間も検索しました。
私はついに完全な問題を見つけました。
- Marshalbyrefobjectを生き生きと維持するためにスポンサーを使用する必要があります。
- その後、 @user626528と同じ問題がありました。オブジェクトは生きていますが、例外がありました。実際には、 私はすべてを「スポンサー」する必要がありましたTransParentProxy「インスタンス, 、そして主なものだけでなく:Sandbox(別のAppDomain)で作成された私のメインオブジェクトは、他のMarshalbyRefobjectsへの参照を返します。
これが完全な説明とユースケースです。
私のクラス「ローダー」は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.
}
}
}
プラグインプロキシタイプには、実際のプラグインタイプへの参照があります。実際、プラグインプロキシはプラグイン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));
}
}
解決するのは難しい問題でした。これが役立つことを願っています!
参考文献:
- MSDN: iLeaseインターフェイス
- MSDN: objrefクラス
- マイクロソフト: VisualC#を使用して、参照によりオブジェクトをリモートサーバーにマーシャリングする方法
スタックオーバーフロー: リモートスポンサーが呼び出されるのを停止します
スタックオーバーフロー: ISPONSORとILEAESEインターフェイスはどのように機能しますか?
- スタックオーバーフロー: 2つの.NET AppDomainsの間で未知のタイプを渡す方法は?
- スタックオーバーフロー: AppDomainとMarshalbyRefobject Life Time:RemotingExceptionを回避する方法は?
- スタックオーバーフロー : Marshalbyrefobjectがスポンサーになっている間も「サーバーで切断される」ようになる
これは私たちのために起こりました。なぜなら、私たちのクラスの1つにタイプのAppDomainの静的変数があったからです。クラスは、長期にわたるWindowsサービスで使用されていました。 AppDomainには、このようにオーバーライドする必要があるInitialIzeLifetimeServiceメソッドがあります。
public override object InitializeLifetimeService(){
return null;
}
私たちは、これを常にロジック外部ロジックをカスタムビルドするためにいくつかのDLLをロードおよびアンロードしたプライベート変数として使用していました。答えはここから取られました: MSDN回答
生産時にこれを変更することができなかったため、試行錯誤により数日であることがわかった静的アプリダメイン変数の寿命よりも短いランダムな間隔でWindowsサービスを再起動する妥協点で終了しました。
この質問は、生涯についてのいくつかのことを明確にするのにも役立ちました。 Stackoverflow-question
私の場合、問題は、クライアントコンピューターに、仮想ネットワークアダプターがアクティブになり、仮想ネットワークアダプターを無効にしたことで、問題が解決したことでした。
私の場合、これはでSQL localDBが保存されていることで起こっていました App_Data
内部のフォルダー Web
事業。パッケージコンソールを使用して実行しようとするたびに update-database
移行を使用してエンティティフレームワークデータベースを開始するために、何も起こりません。しばらくすると、そのエラーが発生します。
ファイル許可を改訂することでこれを解決しました App_Data
. 。一度固定されたら、出来上がり、うまくいきました。
この質問はそうです すでにStackoverflowについて詳細に答えられています. 。 TL/DR:
- Singleton Semantics Overrideが必要な場合
InitializeLifetimeService
nullを返すには - 使用する
ClientSponsor
オブジェクトをより長く生き続けるために。