"L'oggetto è stato disconnesso o non esiste al server" Eccezione
-
27-10-2019 - |
Domanda
Devo usare le chiamate incrociate nella mia app, e talvolta ho questa Exception da remoto:
Oggetto '/2fa53226_da41_42ba_b185_ec7d9c454712/ygiw+xfegmkhdinj7g2kpkhc_7.rem' è stato disconnesso o non esiste sul server.
L'oggetto target è ancora vivo, l'ho controllato.
UPD Ho impostato Breakpoint nel finalizer dell'oggetto target e non colpisce mai. Quindi, questo oggetto è vivo e non è stato gc.
Soluzione
Ciò è probabilmente dovuto al fatto che il collettore di immondizia locale sul lato server raccoglie l'oggetto. Puoi impedirlo rinnovando il leasing. Puoi leggere di più su questo in questi articoli:
- Gestire la vita di oggetti .NET remoti con leasing e sponsorizzazione
- CLR Inside Out: Gestione della vita degli oggetti
Aggiornare: Sfortunatamente, la rivista MSDN di 2008 o più non è più sfogliabile online, ma solo come file .chm che devi scaricare sulla macchina locale. I problemi precedenti sono disponibili in:
- Gestire la vita di oggetti .NET remoti con leasing e sponsorizzazione in Numero di dicembre 2003
- Clr Inside Out: Gestione della vita degli oggetti in Novembre 2007 Numero
Altri suggerimenti
Questo perché la gestione a vita sul lato server disconnette l'oggetto quando il contratto di locazione scade, per consentire a GC di raccoglierlo. Se provi a usarlo dal lato client, otterrai un'eccezione, anche se non è stato ancora GC sul server (ad esempio perché c'è ancora un altro riferimento ad esso) ma il contratto di locazione è scaduto. Questo per evitare comportamenti imprevedibili. La risposta accettata fornisce un buon riferimento su come gestire correttamente la durata degli oggetti .NET remoti.
Ho avuto lo stesso problema e ho cercato molte ore con l'aiuto di molti post di StackOverflow.
Ho finalmente trovato il problema completo.
- Devo usare uno sponsor per mantenere vivo il mio MarshalByreFobject.
- Ho quindi avuto lo stesso problema di @user626528: l'oggetto è vivo ma ho avuto l'eccezione. Infatti, Avevo bisogno di "sponsorizzare" tutto "TransparentProxy"istanze, e non solo quello principale: il mio oggetto principale creato in sandbox (un altro appdomain) restituisce riferimenti ad altri MarshalbyreFobjects.
Ecco la spiegazione completa e il caso d'uso:
La mia classe "Loader" eredita da MarshalByReFobject e lo tengo vivo con una classe isponsor. So che "clientponsor" esiste in .NET, ma non avevo modo di determinare se e quando il rinnovo () viene chiamato, quindi ho fatto la mia classe con l'aiuto della comunità di Stackoverflow (leggi commenti sul codice):
/// <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);
}
}
E poi ho usato questo "sponsor personalizzato" come questo:
// 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.
}
}
}
Il tipo pluginproxy ha un riferimento al tipo di plug -in reale. In effetti, la pluginProxy è instanziata all'interno del plug -in AppDomain e restituita al principale appdomain, per consentirgli di chiamare i plugin anche se ignora il loro tipo reale. Quindi il pluginproxy, per essere accessibile dal principale appdomain, deve essere serializzato per incrociare i limiti di appdomain. Ho avuto un problema perché non ho sponsorizzato questi 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));
}
}
È stato un sacco di problemi da risolvere, spero che questo aiuti!
Riferimenti:
- MSDN: ILEase Interface
- MSDN: Classe Objref
- Microsoft: Come marshall un oggetto a un server remoto per riferimento utilizzando Visual C#
Stackoverflow: Lo sponsor remotante smette di essere chiamato
Stackoverflow: Come funzionano le interfacce iSponsor e Ilease?
- Stackoverflow: Come passare un tipo sconosciuto tra due appdomains .net?
- Stackoverflow: AppDdomain e MarshalByReFobject Life Time: come evitare la remoto Exception?
- Stackoverflow: MarshalByreFobject diventa "disconnesso al server" anche se sponsorizzato
Questo è successo per noi perché avevamo una variabile statica in una delle nostre classi che era di tipo appdomain. La classe è stata utilizzata in un servizio Windows a lungo in esecuzione. AppDomain ha un metodo InitialiSEtimeservice che deve essere annullato in questo modo:
public override object InitializeLifetimeService(){
return null;
}
Usammo costantemente questo come variabile privata che caricava e scaricava alcune DLL per la logica esterna su misura. La risposta è stata presa da qui: Risposta MSDN
Poiché non siamo stati in grado di modificarlo a tempo di produzione, abbiamo concluso con un compromesso di riavvio del servizio Windows a intervalli casuali che sono più brevi della durata della variabile appdomain statica che per prova ed errore abbiamo scoperto che sono diversi giorni.
Questa domanda ha anche contribuito a chiarire alcune cose sulla vita: Domanda stackoverflow
Nel mio caso, il problema era che nel computer client era attivo un adattatore di rete virtuale, disabilitando gli adattatori di rete virtuale, il problema è stato risolto
Nel mio caso, questo stava accadendo con SQL LocalDB memorizzato App_Data
cartella all'interno Web
progetto. Ogni volta che provo a utilizzare il pacchetto per eseguire update-database
Per iniziare il database del mio framework entità utilizzando le migrazioni, non succede nulla. Poi dopo un po ', ottengo quell'errore.
L'ho risolto rivedendo le autorizzazioni App_Data
. Una volta risolto, voilà, ha funzionato.
Questa domanda è stata ha risposto in modo molto dettagliato già su StackOverflow. Tl/dr:
- Se vuoi Singleton Semantics Override
InitializeLifetimeService
per restituire null - Uso
ClientSponsor
per mantenerti in vita più a lungo.