2

我尝试了以下非常好的教程https://www.eidias.com/blog/2013/7/26/plugins-in-wpf-mvvm-with-mef#cm-249迁移到 MEF2 但由于某种原因组件未显示在目录中。从 MEF2 我想使用 API 配置(RegistrationBuilder 类)(这里是一个例子:https ://stefanhenneken.wordpress.com/2013/01/21/mef-teil-11-neuerungen-unter-net-4-5/ ),也许有人知道如何将 MEF2 正确应用于本教程。非常感谢你。

这里是解决方案的概述: 解决方案概述

在 MainViewModel.cs 我还不知道如何将导入集成到RegistrationBuilder.Can 你检查其余的代码?谢谢。

namespace WPF_MEF_App
{
    public class MainWindowModel : NotifyModelBase
    {
        public ICommand ImportPluginCommand { get; protected set; }
        private IView PluginViewVar;

       [Import(typeof(IView), AllowRecomposition = true, AllowDefault = true)]
        public IView PluginView
        {
            get { return PluginViewVar; }
            set{ PluginViewVar = value; NotifyChangedThis();}
        }

        [ImportMany(typeof(IView), AllowRecomposition = true)]
        public IEnumerable<Lazy<IView>> Plugins;

        private AggregateCatalog catalog;
        private CompositionContainer container;

        public MainWindowModel()
        {
            ImportPluginCommand = new DelegateCommand(ImportPluginExecute);
            RegistrationBuilder builder = new RegistrationBuilder();
            builder.ForType<PluginSecondScreen>()
                .Export<IView>(eb =>
                {
                    eb.AddMetadata("Name", "PluginSecond");
                })
                .SetCreationPolicy(CreationPolicy.Any);
            //.ImportProperties(pi => pi.Name == "IView",
            //        (pi, ib) => ib.AllowRecomposition());

            builder.ForType<CalculatorScreen>()
                .Export<IView>(eb =>
                {
                    eb.AddMetadata("Name", "CalculatorScreen");
                })
                .SetCreationPolicy(CreationPolicy.Any);
                //.ImportProperties(pi => pi.Name == "IView",
                //        (pi, ib) => ib.AllowRecomposition());

            catalog = new AggregateCatalog();

            string pluginsPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            catalog.Catalogs.Add(new DirectoryCatalog(pluginsPath, "Plugin*.dll"));
            catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly(), builder));

            //also we add to a search path a subdirectory plugins
            pluginsPath = Path.Combine(pluginsPath, "plugins");
            if (!Directory.Exists(pluginsPath))
                Directory.CreateDirectory(pluginsPath);
            catalog.Catalogs.Add(new DirectoryCatalog(pluginsPath, "Plugin*.dll"));            

            //Create the CompositionContainer with the parts in the catalog.
            container = new CompositionContainer(catalog);
        }

        private void ImportPluginExecute()
        {
            //refresh catalog for any changes in plugins
            //catalog.Refresh();

            //Fill the imports of this object
            //finds imports and fills in all preperties decorated
            //with Import attribute in this instance
            container.ComposeParts(this);
            //another option
            //container.SatisfyImportsOnce(this);
        }
    }
}

这是两个插件:我已经在这里评论了导出,因为RegistrationBuilder. 在此处输入图像描述 在此处输入图像描述

4

1 回答 1

1

我检查了你的尝试。需要改进的几点。

  1. 通常,您应该在中心位置配置容器,通常在启动时的应用程序入口点,例如在 App.xaml.cs 的方法内。一个类从不创建自己的容器来导入其依赖项。如果这是必要的,请考虑导入一个ExportFactory<TImport>永远不要绕过容器)。
  2. 您必须通过构造函数(推荐)或属性不是字段来导入依赖项。因此,您需要将 and 添加get到 andset的定义PluginViewPlugins
  3. 您应该使用基于注释的依赖解析或基于 API。不要混合它。因此,您必须ImportMainWindowModel.
  4. 您不能有多个接口的实现,例如IView和单个导入(基数)。您应该导入一个具体类型的集合,只注册一个具体类型,或者为每个具体类型(例如PluginSecondScreenICalculatorScreen)引入一个专用接口,其中每个接口都继承共享接口(例如IView)。
  5. CompositionContainer完成初始化后不要忘记处理。
  6. SetCreationPolicy(CreationPolicy.Any)是多余的,因为CreationPolicy.Any默认值通常默认为CreationPolicy.Shared.
  7. 尝试在任何地方使用接口
  8. string使用类或类成员或类型名称时避免使用文字。改用nameof
    ImportProperties(pi => pi.Name == "Plugins")
    应该是:
    ImportProperties(pi => pi.Name == nameof(MainWindowModel.Plugins)。这使得重构变得容易得多。

主窗口模型.cs

class MainWindowModel
{
  // Import a unique matching type or import a collection of all matching types (see below).
  // Alternatively let the property return IView and initialize it form the constructor,
  // by selecting an instance from the `Plugins` property.
  public IPluginSecondScreen PluginView { get; set; }

  // Import many (implicit)
  public IEnumerable<Lazy<IView>> Plugins { get; set; }
}

IView用于创建独特类型的接口和专业化:

interface IView
{
}

interface IPluginSecondScreen : IView
{
}

interface ICalculatorScreen : IView
{
}

接口实现:

class PluginSecondScreen : UserControl, IPluginSecondScreen
{
}

class CalculatorScreen : UserControl, ICalculatorScreen
{
}

Application.Startup使用事件处理程序从 App.xaml.cs 初始化应用程序:

private void Run(object sender, StartupEventArgs e)
{
  RegistrationBuilder builder = new RegistrationBuilder();
  builder.ForTypesDerivedFrom<IView>()
    .ExportInterfaces();

  builder.ForType<MainWindowModel>()
    .Export()
    .ImportProperties(
      propertyInfo => propertyInfo.Name.Equals(nameof(MainWindowModel.Plugins), StringComparison.OrdinalIgnoreCase) 
        || propertyInfo.Name.Equals(nameof(MainWindowModel.PluginView), StringComparison.OrdinalIgnoreCase));

  var catalog = new AggregateCatalog();
  catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly(), builder));
  catalog.Catalogs.Add(new DirectoryCatalog(Environment.CurrentDirectory, "InternalShared.dll", builder));
  catalog.Catalogs.Add(new DirectoryCatalog(Environment.CurrentDirectory, "PluginCalculator.dll", builder));
  catalog.Catalogs.Add(new DirectoryCatalog(Environment.CurrentDirectory, "PluginSecond.dll", builder));

  using (var container = new CompositionContainer(catalog))
  {    
    MainWindowModel mainWindowModel = container.GetExportedValue<MainWindowModel>();

    this.MainWindow = new MainWindow() { DataContext = mainWindowModel };
    this.MainWindow.Show();
  }
}
于 2020-03-28T18:39:17.187 回答