【问题标题】:MAF (System.Addin) property of serializable type in contract?合同中可序列化类型的MAF(System.Addin)属性?
【发布时间】:2012-11-19 10:42:37
【问题描述】:

我们正在测试 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; }
}

【问题讨论】:

  • 您应该仔细阅读以下由负责 System.AddIn 的团队成员撰写的博客条目blogs.msdn.com/b/clraddins/archive/2007/02/27/… TheValue 应该是合同程序集中的结构。顺便说一句,我已经广泛使用 System.AddIn 几年了,除了将主机暴露给加载项(如 Visual Studio、MS Office 等)外,我不推荐它。如果您想创建更复杂的设计,使用协作的插件或乐高设计,那么 MEF 或 Mono.AddIns 是更好的选择。
  • 您能否通过添加加载和初始化加载项的代码来更新您的问题?
  • 之所以使用MAF是因为我们认为我们需要隔离appdomains。我们必须支持同时运行同一个插件的新旧版本。我们还将看看 Mono Addins。更新了问题中的代码以包括加载。
  • 如果我们使用struct,我们会得到同样的结果。
  • 你在插件的Start方法中使用了IHostContract.SetCurrent? Start 中的工作是否同步?顺便说一句,如果没有加载一个加载项,这是不安全的。

标签: c# .net maf system.addin


【解决方案1】:

您需要遵循lifetime management 的准则。在每个合同到视图适配器中,您需要存储一个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% isolation 仅适用于加载项在与主机不同的进程中加载​​的情况。

【讨论】:

  • 谢谢!就是这样。我不认为我们可以避免 appdomains 的隔离。我们会有一个windows,需要持续运行的服务,加载和卸载插件。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-11
  • 1970-01-01
  • 1970-01-01
  • 2020-10-18
  • 1970-01-01
相关资源
最近更新 更多