AppDomain await async Task prevent SerializationException
本文关键字:prevent SerializationException Task async await AppDomain | 更新日期: 2023-09-27 18:04:01
我有一个windows服务,它在运行时加载另一个AppDomain中的程序集。然后执行它们并最终卸载AppDomain。问题是执行方法从插件是异步任务,我得到SerializationException,因为任务不继承从MarshalByRefObject。
我包装插件在代理继承从MarshalByRefObject,但我不知道如何摆脱SerializationException?
public interface IPlugin : IDisposable
{
Guid GUID { get; }
string Name { get; }
string Description { get; }
Task Execute(PluginPanel panel, string user);
}
代理:[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); }
}
以及加载程序集并获得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);
}
也许我加载插件的整个概念是错误的?
感谢Hamlet Hakobyan和Stephen Toub的帖子,我想我能够解决这个问题。
我替换了来电者的线路
try { tExe = pProxy.Execute(panel, user.Username); }
tExe = DoWorkInOtherDomain(pProxy, panel, user.Username);
和方法DoWorkInOtherDomain:
private Task DoWorkInOtherDomain(PluginProxy pProxy, PluginPanel panel, string user)
{
var ch = new MarshaledResultSetter<string>();
pProxy.Execute(panel, user, ch);
return ch.Task;
}
最后是代理类:
Task.Run(() =>
{
try
{
m_Plugin.Execute(panel, user).Wait();
}
catch (AggregateException e)
{
if (e.InnerExceptions != null)
foreach (Exception ein in e.InnerExceptions)
AddToErrorLog(panel.PanelName, ein);
}
catch (Exception e) { AddToErrorLog(panel.PanelName, e); }
finally { ch.SetResult(AppDomain.CurrentDomain.FriendlyName); }
});
我需要在
中调用Wait()m_Plugin.Execute(panel, user).Wait();
它从插件捕获异常,所以一切都很好。Wait()调用应该只阻塞Task。运行而不是其他任务
谁能告诉我这是一个好的解决方案,还是我应该改变一些东西?我不需要结果,所以我只需要: ch.SetResult(AppDomain.CurrentDomain.FriendlyName);
因为我不知道如果没有结果我该怎么做