AppDomain等待异步任务阻止SerializationException [英] AppDomain await async Task prevent SerializationException

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

问题描述

我有一个Windows服务,该服务在运行时将程序集加载到另一个AppDomain中.然后执行它们,最后卸载AppDomain.问题是来自插件的execute方法是异步任务,由于没有从MarshalByRefObject继承Task,所以我得到了SerializationException.

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); }
        });

我需要在其中调用Wait()

I need to call Wait() in

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

它从插件捕获异常,所以一切都很好. Wait()调用应仅阻止Task.Run,​​而不阻止其他Tasks.

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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆