代码之家  ›  专栏  ›  技术社区  ›  Sorskoot

什么是MEF最佳实践?[关闭]

  •  23
  • Sorskoot  · 技术社区  · 15 年前

    在代码中使用MEF的最佳实践是什么?在启动可扩展应用程序时,是否需要考虑任何陷阱?你有没有遇到什么早该知道的事?

    3 回复  |  直到 15 年前
        1
  •  7
  •   Scott Whitlock    15 年前

    我正在MEF上构建一个完全成熟的可扩展应用程序(并将WPF与MVVM模式一起使用)。我将构建和开放源代码IT的基本应用程序框架作为 SoapBox Core . 我还在代码项目上发布了一个基于Soapbox核心的演示: Building an Extensible Application with MEF, WPF, and MVVM .

    我不确定使用MVVM是否适用于您,但如果适用,那么通过查看使用MEF实现MVVM,您可能会学到很多东西。尤其是它导入视图的方式。

    就最佳实践而言…我创建了一个扩展的嵌套层次结构(所以基本模块称为主机,它所做的只是组成应用程序并导入一些基本扩展)。然后,这些扩展公开了其他扩展点,并且应用程序在运行它时会自行构建(组合和扩展之间的交叉)。

    为了保持所有的内容都是正确的,我将扩展层次结构放入一组静态类中。例如,核心框架提供的所有扩展点如下:

    namespace SoapBox.Core.ExtensionPoints
    {
        public static class Host
        {
            public const string Styles = "ExtensionPoints.Host.Styles";
            public const string Views = "ExtensionPoints.Host.Views";
            public const string StartupCommands = "ExtensionPoints.Host.StartupCommands";
            public const string ShutdownCommands = "ExtensionPoints.Host.ShutdownCommands";
        }
        public static class Workbench
        {
            public const string ToolBars = "ExtensionPoints.Workbench.ToolBars";
            public const string StatusBar = "ExtensionPoints.Workbench.StatusBar";
            public const string Pads = "ExtensionPoints.Workbench.Pads";
            public const string Documents = "ExtensionPoints.Workbench.Documents";
    
            public static class MainMenu
            {
                public const string Self = "ExtensionPoints.Workbench.MainMenu";
                public const string FileMenu = "ExtensionPoints.Workbench.MainMenu.FileMenu";
                public const string EditMenu = "ExtensionPoints.Workbench.MainMenu.EditMenu";
                public const string ViewMenu = "ExtensionPoints.Workbench.MainMenu.ViewMenu";
                public const string ToolsMenu = "ExtensionPoints.Workbench.MainMenu.ToolsMenu";
                public const string WindowMenu = "ExtensionPoints.Workbench.MainMenu.WindowMenu";
                public const string HelpMenu = "ExtensionPoints.Workbench.MainMenu.HelpMenu";
            }
        }
    
        public static class Options
        {
            public static class OptionsDialog
            {
                public const string OptionsItems = "ExtensionPoints.Options.OptionsDialog.OptionsItems";
            }
        }
    }
    

    因此,如果希望扩展名向“文件”菜单中添加某些内容,则可以导出实现合同名为soapbox.core.extensionpoints.workbench.mainmenu.file menu的IMenuItem的内容。

    每个扩展都有一个“id”,它只是一个字符串标识符。这些现有ID在另一个层次结构中定义:

    namespace SoapBox.Core.Extensions
    {
        public static class Workbench
        {
            public static class MainMenu
            {
                public const string File = "File";
                public const string Edit = "Edit";
                public const string View = "View";
                public const string Tools = "Tools";
                public const string Window = "Window";
                public const string Help = "Help";
    
                public static class FileMenu
                {
                    public const string Exit = "Exit";
                }
    
                public static class ViewMenu
                {
                    public const string ToolBars = "ToolBars";
                }
    
                public static class ToolsMenu
                {
                    public const string Options = "Options";
                }
            }
        }
    }
    

    正如你所看到的,filemenu已经包含了一个出口扩展(这是为关闭应用程序而预先编程的)。如果要向“文件”菜单添加扩展名,您可能希望它出现在“退出”菜单项之前。IMenuItem继承自IExtension,它有两个属性:

    • 插入相对ID
    • 前后

    因此,您的扩展将为insertrelativeID返回soapbox.core.extensions.workbench.mainmenu.filemenu.exit,并为beforeafter属性(枚举)返回beforeafter。当工作台导入所有文件菜单扩展名时,它将根据这些ID对所有内容进行排序。这样,以后的扩展就相对于现有的扩展插入自己。

        2
  •  6
  •   Huds0nHawk Glen    15 年前

    最佳实践是使用共享(单例)模型。这就给我们带来了设计考虑,建议您将可导出部分设计为无状态和线程安全的,这样它们就不会受到同一实例上的多个调用(可能在不同线程上)的影响。

    如果单例模型不适合您,建议使用构建器模式(将导出的部分与实际的实例化分开)。 您应该记住,使用非共享模型是非常昂贵的,因为它使用反射来进行实际的实例化(通过使用构建器模式,您可以以更少的代价获得相同的结果)。

    也看这里 http://blogs.microsoft.co.il/blogs/bnaya/archive/2010/01/09/mef-for-beginner-toc.aspx 当然,你知道你可以在这里找到信息: http://mef.codeplex.com

        3
  •  2
  •   Community CDub    8 年前

    我对MEF还是很陌生,但我想在这场讨论中添加更多内容,因为当我试图弄明白为什么事情不能按我期望的方式工作时,我会不断经历地狱。

    首先,在使用MEF时,我建议将System.ComponentModel.Composition添加到解决方案中,而不仅仅是添加对程序集的引用。尽管在MEF中调试问题感觉像是一场递归的噩梦,但当您无法找出问题所在时,这是绝对不可避免和至关重要的。

    这就引出了我的下一点,那就是永远不要忘记,MEF不知道你不告诉它什么,或者,如果你不正确地告诉它, 。例如,我的alpha应用程序与mef一起工作得很好——我让它在主GUI中组成部分,容器加载的所有程序集(它们是主应用程序的依赖项)都导出了必要的接口。事情进展顺利,我能够让MEF在我想要的时间和地点解决实例。

    但是,我刚开始研究下一个版本;一些插件被加载(那些导出了接口,但没有导入要求的插件),而其他插件则没有(那些确实需要导入的插件)。这一次,我在负责加载插件的ApplianceManager类中编写了一些部分,但是插件需要解析来自应用程序中其他类(在我的例子中是模型)的导入。我认为这应该是自动发生的,特别是在目录结构中可以看到那些程序集被检测到…但我还是不能让它工作…这让我回到了我的第一点——添加源代码,而不仅仅是程序集。调试这个几乎让我发疯,但我最终会在努力地逐步通过MEF代码之后发现它。:

    我想看到有人对这个问题给出了一个答案,这个问题讨论了易于MEF集成的体系结构。关于工具栏菜单之类的问题的答案确实很好,但是我想看到一些关于完全驻留在MVVM模型侧的东西的讨论。比如插件管理器、数据库、插件和共享库的排列方式。我仍在试图弄明白为什么我有一个相当好的时间让我的第一个mef应用程序工作,但在获得更多的“经验”后,我不能让我的新应用程序100%工作。

    更新2010-06-09

    我想增加另一个可能的做法,以帮助您导航通过mef灌木。今天,我需要对那个项目做一个我“只是不知道”的健全检查,所以我创建了一个简单的、非常规的UML类图,在这个类图中,我使用依赖项来标记导入和导出。以下是我发现的,这使问题非常清楚。

    工作应用:

    非工作应用:

    那不是很愚蠢吗?模型没有装载,它自己就在一个岛上。我相信这就是为什么我基于MEF的依赖关系没有得到解决的原因(如果有人可以纠正我,如果我在这里是错的,我会感激,尽管!)

    当我试图弄明白为什么事情不能像我期望的那样运作的时候,我常常会下地狱。

    首先,在使用MEF时,我建议将System.ComponentModel.Composition添加到解决方案中,而不仅仅是添加对程序集的引用。尽管在MEF中调试问题感觉像是一场递归的噩梦,但当您无法弄清楚到底出了什么问题时,这是绝对不可避免的,也是至关重要的。

    这让我进入下一个观点,那就是永远不要忘记,我不知道你不告诉我什么,或者 如果你说得不恰当 . 例如,我的alpha应用程序与mef一起工作得很好——我让它在主GUI中组成部分,容器加载的所有程序集(它们是主应用程序的依赖项)都导出了必要的接口。事情进展顺利,我能让MEF在我想要的时间和地点解决问题。

    但是,我刚开始研究下一个版本;一些插件被加载(那些导出了接口,但没有导入要求的插件),而其他插件则没有(那些确实需要导入的插件)。这一次,我在负责加载插件的ApplianceManager类中编写了一些部分,但是插件需要解析来自应用程序中其他类(在我的例子中是模型)的导入。我认为这应该是自动发生的,特别是在目录结构中可以看到那些程序集被检测到…但我还是不能让它工作…这让我回到了我的第一点——添加源代码,而不仅仅是程序集。调试这个几乎让我发疯,但我最终会在努力地逐步通过MEF代码之后发现它。:)

    我想看到有人对这个问题给出了一个答案,这个问题讨论了易于MEF集成的体系结构。关于工具栏菜单之类的问题的答案确实很好,但是我想看到一些关于完全驻留在MVVM模型侧的东西的讨论。比如插件管理器、数据库、插件和共享库的排列方式。我仍在试图弄明白为什么我有一个相当好的时间让我的第一个mef应用程序工作,但在获得更多的“经验”后,我不能让我的新应用程序100%工作。

    更新2010-06-09

    我想增加另一个可能的做法,以帮助您导航通过mef灌木。今天,我需要对那个项目做一个我“只是不知道”的健全检查,所以我创建了一个简单的、非常规的UML类图,在这个类图中,我使用依赖项来标记导入和导出。这是我发现的,这使问题非常清楚。

    工作应用: alt text

    非工作应用: alt text

    那不是很愚蠢吗?模型没有装载,它自己就在一个岛上。我 相信 这就是我基于MEF的依赖关系没有得到解决的原因(如果有人能纠正我,如果我在这里是错的,我会很感激!)