0

我们正在测试 MAF 插件以用作我们的插件框架。但我们陷入了一个基本问题。我们可以使用可序列化类型作为 IContract 参数吗?

合同和参数类型都在同一个程序集中定义:

    public interface IHostContract : IContract
    {
        void SetCurrent(TheValue tagValue);   // does not work
        void SetCurrentSimple(double value);  // works fine
    }

    [Serializable]
    public sealed class TheValue
    {
       public int Id { get; set; }

       public double Value { get; set; }
    }

我们能够让一切正常运行。调用 SetCurrent 会导致异常: AppDomainUnloadedException :

The application domain in which the thread was running has been unloaded.

Server stack trace: 
   at System.Threading.Thread.InternalCrossContextCallback(Context ctx, IntPtr ctxID, Int32 appDomainID, InternalCrossContextDelegate ftnToCall, Object[] args)
   at System.Runtime.Remoting.Channels.CrossAppDomainSink.DoTransitionDispatch(Byte[] reqStmBuff, SmuggledMethodCallMessage smuggledMcm, SmuggledMethodReturnMessage& smuggledMrm)
   at System.Runtime.Remoting.Channels.CrossAppDomainSink.SyncProcessMessage(IMessage reqMsg)

Exception rethrown at [0]: 

加载和运行插件:

public void Run(string PluginFolder)
{
    AddInStore.Rebuild(PluginFolder);
    Collection<AddInToken> tokens = AddInStore.FindAddIns(typeof(Plugins.IPlugin), PluginFolder);

    foreach (var token in tokens)
    {
        Console.WriteLine("Found addin: " + token.Name + " v" + token.Version);
        try
        {
            var plugin = token.Activate<Plugins.IPlugin>(AddInSecurityLevel.FullTrust);
            plugin.PluginHost = this;
            plugin.Start();
            plugin.Stop();
        }
        catch (Exception exception)
        {
            Console.WriteLine("Error starting plugin: " + exception.Message);
        }
    }
}

插入:

[System.AddIn.AddIn("Plugin1", Version = "1.0.0")]
public class Plugin1 : IPlugin
{
    private int started;

    public Plugin1()
    {
        Console.WriteLine("Plugin 1 created");
    }

    public void Start()
    {
        Console.WriteLine("Plugin 1 started: {0}", started);
        started++;

        var tagValue = new TheValue { Id = 1, Value = 4.32 };
        PluginHost.SetCurrent(tagValue);
    }

    public void Stop()
    {
        Console.WriteLine("Plugin 1 stopped");
    }

    public IPluginHost PluginHost { get; set; }
}
4

1 回答 1

2

您需要遵循生命周期管理指南。在每个 contract-to-view 适配器中,您需要存储一个ContractHandle。这对于隐式创建的代理的生命周期管理是必要的System.AddIn(请记住,System.AddIn它基于 .NET Remoting)。

取自 MSDN:

ContractHandle 对于生命周期管理至关重要。如果您未能保留对 ContractHandle 对象的引用,垃圾收集将回收它,并且当您的程序不期望它时,管道将关闭。这可能会导致难以诊断的错误,例如 AppDomainUnloadedException。关闭是管道生命周期中的正常阶段,因此生命周期管理代码无法检测到这种情况是错误的。

如果您决定System.AddIn在您的应用程序中使用,那么您需要PipelineBuilder。在讨论区中,您将找到有关如何使其与 VS2010 一起工作的帮助(这很简单)。我想让它与 VS2012 一起工作并不难。此工具将为您处理所有 System.AddIn 错综复杂的问题。您需要做的就是创建合同,然后PipelineBuilder为您创建管道的其余部分。它还将确保您遵循有关如何构建合同的指南,这是 System.AddIn 最重要的事情。

在决定使用插件框架之前,不要忘记查看MEFMEF可以与Autofac一起使用并通过适配器提供版本控制。恕我直言,任何人都应该选择 System.AddIn 的唯一原因是隔离功能。但请注意,只有在加载项与主机不同的进程中加载​​时,才能实现 100%隔离。

于 2012-11-19T22:30:59.270 回答