本文介绍了AppDomain 等待异步任务阻止 SerializationException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 Windows 服务,它在运行时在另一个 AppDomain 中加载程序集.然后它执行它们并最终卸载 AppDomain.问题是插件的 execute 方法是异步任务,我得到了 SerializationException,因为 Task 不是从 MarshalByRefObject 继承的.

I have a windows service, which loads assembly in another AppDomain at runtime. Then it executes them and finally unloads the AppDomain. The problem is the execute method from the plugins are async tasks and I get the SerializationException because Task does not inherit from MarshalByRefObject.

我将插件包装在继承自 MarshalByRefObject 的代理中,但我不知道如何摆脱 SerializationException?

I wrapped the plugin in a proxy which inherits from MarshalByRefObject, but I dont know how to get rid of the 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 的方法:

And the Method which loads the Assembly and gets the 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);
           }

也许我加载插件的整个概念是错误的?

Maybe my whole concept of loading Plugins is wrong?

推荐答案

感谢 Hamlet Hakobyan 和 Stephen Toub 的帖子,我想我能够解决问题.

Thanks to Hamlet Hakobyan and the post from Stephen Toub, I think I was able to solve the problem.

我替换了来电者的线路

 try { tExe = pProxy.Execute(panel, user.Username); }

 tExe = DoWorkInOtherDomain(pProxy, panel, user.Username);

和 DoWorkInOtherDomain 方法:

and the method 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); }
        });

我需要在

m_Plugin.Execute(panel, user).Wait();

它从插件中捕获异常,所以一切正常.Wait() 调用应该只阻塞 Task.Run 而不是其他任务.

it catches the Exceptions from the plugin so everything is doing fine. The Wait() call should only blocking the Task.Run and not the other Tasks.

谁能告诉我这是一个好的解决方案还是我应该改变一些东西?我不需要结果,所以我只需要:

Can anyone tell me if this is a good solution or should I change something? I dont need a result so I just do:

 ch.SetResult(AppDomain.CurrentDomain.FriendlyName);

因为没有结果我不知道该怎么做.

because I dont know how I should do it without a result.

这篇关于AppDomain 等待异步任务阻止 SerializationException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-28 07:03