代码改变世界

可扩展编程MEF学习笔记

2021-05-07 18:51  .net小跟班(杜)  阅读(97)  评论(0编辑  收藏  举报

MEF,全称Managed Extensibility Framework(托管可扩展框架)

对MEF有这样一段说明:

  Managed Extensibility Framework 或 MEF 是一个用于创建可扩展的轻型应用程序的库。 应用程序开发人员可利用该库发现并使用扩展,而无需进行配置。 扩展开发人员还可以利用该库轻松地封装代码,避免生成脆弱的硬依赖项。 通过 MEF,不仅可以在应用程序内重用扩展,还可以在应用程序之间重用扩展。

主要原理是C#的依赖注入原则

代码层级关系

新建Bootstrapper类继承至BootstrapperBase:

public class Bootstrapper:BootstrapperBase
    {
        private CompositionContainer m_container;
        public Bootstrapper()
        {
            Initialize();
        }
        protected override void OnStartup(object sender, StartupEventArgs e)
        {
            DisplayRootViewFor<PopupWindowViewModel>();
        }

        protected override void Configure()
        {
            m_container = new CompositionContainer(new AggregateCatalog(AssemblySource.Instance.Select(x=>new AssemblyCatalog(x))));
            var batch = new CompositionBatch();
            batch.AddExportedValue<IWindowManager>(new WindowManager());//新建一个窗口管理器添加到IOC中
            batch.AddExportedValue<IEventAggregator>(new EventAggregator());//弱事件添加
            batch.AddExportedValue(m_container);
            m_container.Compose(batch);
        }
        /// <summary>
        /// IOC容器获取实例的方法
        /// </summary>
        /// <param name="service"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        protected override object GetInstance(Type service, string key)
        {
            string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(service) : key;
            var exports = m_container.GetExportedValues<object>(contract);
            var exportList = exports.ToList();//避免直接用exports时 调用2次IEnumerable操作
            if (exportList.Any())
            {
                return exportList.First();
            }
            throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
        }

        /// <summary>
        /// IOC容器获取实例的方法
        /// </summary>
        /// <param name="service"></param>
        /// <returns></returns>
        protected override IEnumerable<object> GetAllInstances(Type service)
        {
            return m_container.GetExportedValues<object>(AttributedModelServices.GetContractName(service));
        }

        /// <summary>
        /// IOC容器注入实例的方法
        /// </summary>
        /// <param name="instance"></param>
        protected override void BuildUp(object instance)
        {
            m_container.SatisfyImportsOnce(instance);
        }

        /// <summary>
        /// 设置加载到AssemblySource中的程序集列表
        /// </summary>
        /// <returns></returns>
        protected override IEnumerable<Assembly> SelectAssemblies()
        {
            List<Assembly> list = base.SelectAssemblies().ToList();
            list.AddRange(new List<Assembly> { Assembly.Load("MyDll") });
            return list;
        }

        protected override void OnUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
        {
            e.Handled = true;
        }
    }

VM继承至Screen

[Export(typeof(PopupWindowViewModel))]
    public class PopupWindowViewModel:Screen
    {
        public IWindowManager _WindowManager { get; set; }
        [ImportingConstructor]//必须加入这个,这是因为用MEF在创建ViewModel实例时,有[ImportingConstructor]标记的构造函数的参数会自动使用容器内相应的export对象
        public PopupWindowViewModel(IWindowManager windowManager)
        {
            this._WindowManager = windowManager;
        }
        protected override void OnInitialize()
        {
            _WindowManager.ShowDialog(new MainViewModel());
        }
        public void PopupForm()
        {
            _WindowManager.ShowWindow(new EditUserViewModel(_WindowManager));
        }
    }

实现效果

典型的MEF组合过程

  (1)创建组件目录(如AssemblyCatalog)

  (2)创建组合容器CompositionContainer,组件容器通过组件目录搜索组件的定义

  (3)创建一个组件

  (4)从组件容器获取其它组件功能的定义,然后执行匹配组合

参考链接:https://kb.cnblogs.com/page/97675/