Ho un servizio di Windows, che carica assembly in un altro AppDomain in fase di esecuzione. Quindi li esegue e infine scarica AppDomain. Il problema è che il metodo di esecuzione dei plugin è un'attività asincrona e ottengo SerializationException perché Task non eredita da MarshalByRefObject.AppDomain attende asincronia Attività impedisce SerializationException
Ho avvolto il plugin in un proxy che eredita da MarshalByRefObject, ma non so come sbarazzarmi di SerializationException?
public interface IPlugin : IDisposable
{
Guid GUID { get; }
string Name { get; }
string Description { get; }
Task Execute(PluginPanel panel, string user);
}
Il proxy:
[Serializable()]
public class PluginProxy : MarshalByRefObject, IPlugin
{
private IPlugin m_Plugin;
public bool Init(string file)
{
Assembly ass = Assembly.Load(AssemblyName.GetAssemblyName(file));
if (ass == null || ass.GetTypes() == null || ass.GetTypes().Length == 0)
return false;
foreach (Type type in ass.GetTypes())
{
if (type.IsInterface || type.IsAbstract)
continue;
if (type.GetInterface(typeof(IPlugin).FullName) != null)
{
m_Plugin = (IPlugin)Activator.CreateInstance(type);
return true;
}
}
return false;
}
public Guid GUID { get { return m_Plugin.GUID; } }
public string Name { get { return m_Plugin.Name; } }
public string Description { get { return m_Plugin.Description; } }
// I debugged and found out the error happens AFTER m_Plugin.Execute
// so the method runs well, but the return back to the pProxy.Execute is throwing the SerializationException
public async Task Execute(PluginPanel panel, string user) { await m_Plugin.Execute(panel, user); }
}
e il metodo che carica l'Assemblea e ottiene il SerializationException:
AppDomainSetup setup = new AppDomainSetup();
// some setup stuff
AppDomain dom = AppDomain.CreateDomain(Guid.NewGuid().ToString(), null, setup);
PluginProxy pProxy = (PluginProxy)dom.CreateInstanceFromAndUnwrap(Assembly.GetExecutingAssembly().CodeBase, typeof(PluginProxy).FullName);
pProxy.Init(app.Apppath);
// I await the task later in code, because the user can cancel the execution
try { tExe = pProxy.Execute(panel, user.Username); }
catch (System.Runtime.Serialization.SerializationException e)
{
// runs always in this catch, even if no Exception from the plugin was thrown
}
catch (Exception e) { AddToErrorLog(panel.PanelName, e); }
finally
{
pProxy.Dispose();
AppDomain.Unload(dom);
}
Forse tutto il mio concetto di caricamento di plugin è sbagliato?
Il tuo 'Execute' restituisce anche il' Task'. Nel tuo wrapper attendi "Esegui" in modo sincrono. –
così invece di attendere m_Plugin.Execute (pannello, utente); Dovrei fare questo: m_Plugin.Execute (pannello, utente) ;? O questo: m_Plugin.Execute (pannello, utente) .Wait() ;? Ma l'Wait() bloccherebbe l'intero thread? – Tony
OK, guarda Stephen Toub [post] (https://social.msdn.microsoft.com/Forums/vstudio/en-US/28277f25-5f5d-4b7c-bf1f-402937fc9f31/tasks-across-appdomain). Dovresti apportare piccole modifiche al tuo caso. –