? MEF 插件式开发 - WPF 初体验_蜘蛛资讯网 亚博APP体育官网下载路线⑧⑧,亚博APP体育官网下载路线⑧⑧,亚博娱乐在线登录平台
首页 > 调查 > 正文

包头壳徊纬培训学校_MEF 插件式开发 - WPF 初体验

目录

  • MEF 在 WPF 中的简单应用
    • 加载插件
    • 获取元数据
    • 依赖注入
  • 总结

MEF 在 WPF 中的简单应用

MEF 的开发模式主要适用于插件化的业务场景中,C/S 和 B/S 中都有相应的使用场景,其中包括但不限于 ASP.NET MVCASP WebFormsWPFUWP 等开发框架。当然,DotNet Core 也是支持的。

在上篇文章中,笔者大致讲述如果在控制台程序中创建一个简单的 MEF 应用程序。如果有读者不太清楚,可点击 MEF 插件式开发 - 小试牛刀 进行查看。在本篇博文中,笔者将创建一个简单的 WPF 程序来做一些 MEF 的相关小实验。

首先,我们创建一个工程,工程的目录结构如下图所示


  • MefSample:WPF主程序,用来负责程序相关初始化
  • MefSample.Core:类库,核心接口库,来用约束相关插件的导出接口
  • MefSample.Plugin1:用户控件库,插件1,某一独立具体的业务模块
  • MefSample.Plugin2:用户控件库,插件2,某一独立具体的业务模块
  • MefSample.Service:类库,某一具体服务的实现

我们在相应模块中添加相应代码,来构建一个简单的 MEF 示例程序。

MefSample.Core 中,我们创建一个 IView 的接口,用于插件的导出约束,示例代码如下所示

[Description("视图接口")]
public interface IView
{
}

然后分别在 MefSample.Plugin1MefSample.Plugin2 创建一个 UserControl ,并修改相应的后台代码

MefSample.Plugin1

[Export(typeof(IView))]
public partial class MainView : UserControl,IView
{
    public MainView()
    {
        InitializeComponent();
    }
}

MefSample.Plugin2

[Export(typeof(IView))]
public partial class MainView : UserControl,IView
{
    public MainView()
    {
        InitializeComponent();
    }
}

加载插件

模块加载分两种:实时加载延迟加载,针对模块数量少,内存消耗小的话,我们可以采用常规的加载方式,但是若工程项目较复杂,模块数较多的话,延迟加载是一种不错的选择方式,这里分别针对这两种加载方式进行简单的代码描述

- 常规加载

在主程序 MefSample 中,我们可以采用之前的加载方式来加载所有的插件,示例代码如下所示

public partial class MainWindow : Window
{
    private CompositionContainer container = null;
    public MainWindow()
    {
        InitializeComponent();
    }
    protected override void OnContentRendered(EventArgs e)
    {
        var dir = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory);
        if (dir.Exists)
        {
            var catalog = new DirectoryCatalog(dir.FullName, "*.dll");
            container = new CompositionContainer(catalog);
            try
            {
                container.ComposeParts(this);
            }
            catch (CompositionException compositionEx)
            {
                Console.WriteLine(compositionEx.ToString());
            }

            IEnumerable plugins = container.GetExportedValues();
            foreach (var plugin in plugins)
            {
                this.tab.Items.Add(new TabItem() { Header = plugin.ToString(),Content = plugin });
            }
        }
        base.OnContentRendered(e);
    }

    protected override void OnClosing(CancelEventArgs e)
    {
        container?.Dispose();
        base.OnClosing(e);
    }
}

- 懒加载

要想用到懒加载技术需要借助 Lazy 来实现,稍微将上述代码修改一下就可以了,示例代码如下所示

public partial class MainWindow : Window private CompositionContainer container null public MainWindow InitializeComponent protected override void OnContentRendered EventArgs e var dir new DirectoryInfo AppDomain. CurrentDomain. BaseDirectory if dir. Exists var catalog new DirectoryCatalog dir. FullName, ". dll" container new CompositionContainer catalog try container. ComposeParts this catch CompositionException compositionEx Console. WriteLine compositionEx. ToString IEnumerable lt IView gt plugins container. GetExportedValues lt IView gt foreach var plugin in plugins this. tab. Items. Add new TabItem Header plugin. ToString, Content plugin base. OnContentRendered e protected override void OnClosing CancelEventArgs e container?. Dispose base. OnClosing e lan jia zai yao xiang yong dao lan jia zai ji shu xu yao jie zhu Lazy lai shi xian, shao wei jiang shang shu dai ma xiu gai yi xia jiu ke yi le, shi li dai ma ru xia suo shi

public partial class MainWindow : Window
{
    [ImportMany]
    public Lazy[] plugins { get; set; }

    private CompositionContainer container = null;
    public MainWindow()
    {
        InitializeComponent();
    }
    protected override void OnContentRendered(EventArgs e)
    {
        var dir = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory);
        if (dir.Exists)
        {
            var catalog = new DirectoryCatalog(dir.FullName, "*.dll");
            container = new CompositionContainer(catalog);
            try
            {
                container.ComposeParts(this);
            }
            catch (CompositionException compositionEx)
            {
                Console.WriteLine(compositionEx.ToString());
            }

            foreach (var plugin in plugins)
            {
                this.tab.Items.Add(new TabItem()
                {
                    // 此时 pulugin 对象还未创建,执行 plugin.Value 才会创建该对象
                    Header = plugin.ToString(),
                    Content = plugin.Value
                });
            }
        }
        base.OnContentRendered(e);
    }

    protected override void OnClosing(CancelEventArgs e)
    {
        container?.Dispose();
        base.OnClosing(e);
    }
}

获取元数据

有时,单纯地加载一个插件并不能满足我们的业务需求,我们可能还需要获取一些插件中的元数据来进行相应处理,这个时候我们就需要借助 IMetaData 来满足我们的业务场景需求。
首先,我们在 MefSample.Core 创建一个新的接口,定义为 IMetadata,并创建一个与之对应的自定义属性 CustomExportMetadata ,相关示例代码如下所示

/// 
/// 元数据接口
/// 第二种方法可参考:MetadataViewImplementation 方式
/// 
public interface IMetadata
{
    [DefaultValue(0)]
    int Priority { get; }
    string Name { get; }
    string Description { get; }
    string Author { get; }
    string Version { get; }
}

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class CustomExportMetadata : ExportAttribute, IMetadata
{
    public int Priority { get; private set; }
    public string Name { get; private set; }
    public string Description { get; private set; }
    public string Author { get; private set; }
    public string Version { get; private set; }

    public CustomExportMetadata() : base(typeof(IMetadata))
    {
    }

    public CustomExportMetadata(int priority) : this()
    {
        this.Priority = priority;
    }

    public CustomExportMetadata(int priority, string name) : this(priority)
    {
        this.Name = name;
    }

    public CustomExportMetadata(int priority, string name, string description) : this(priority, name)
    {
        this.Description = description;
    }

    public CustomExportMetadata(int priority, string name, string description, string author) : this(priority, name, description)
    {
        this.Author = author;
    }

    public CustomExportMetadata(int priority, string name, string description, string author, string version) : this(priority, name, description, author)
    {
        this.Version = version;
    }
}

然后为我们的每个插件打上相应标签

MefSample.Plugin2

[Export(typeof(IView))]
[CustomExportMetadata(1,"Plugin 1","这是第一个插件","hippiezhou","1.0")]
public partial class MainView : UserControl,IView
{
    public MainView()
    {
        InitializeComponent();
    }
}

MefSample.Plugin2

[Export(typeof(IView))]
[CustomExportMetadata(2, "Plugin 2", "这是第二个插件", "hippiezhou", "1.0")]
public partial class MainView : UserControl, IView
{
    public MainView()
    {
        InitializeComponent();
    }
}

最后,修改我们的主程序

public partial class MainWindow : Window
{
    [ImportMany]
    public Lazy[] Plugins { get; set; }

    private CompositionContainer container = null;
    public MainWindow()
    {
        InitializeComponent();
    }
    protected override void OnContentRendered(EventArgs e)
    {
        var dir = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory);
        if (dir.Exists)
        {
            var catalog = new DirectoryCatalog(dir.FullName, "*.dll");
            container = new CompositionContainer(catalog);
            try
            {
                container.ComposeParts(this);
            }
            catch (CompositionException compositionEx)
            {
                Console.WriteLine(compositionEx.ToString());
            }

            foreach (var plugin in Plugins)
            {
                this.tab.Items.Add(new TabItem()
                {
                    //获取元数据
                    Header = plugin.Metadata.Name,
                    Content = plugin.Value
                });
            }
        }
        base.OnContentRendered(e);
    }

    protected override void OnClosing(CancelEventArgs e)
    {
        container?.Dispose();
        base.OnClosing(e);
    }
}

依赖注入

最后说一下关于依赖注入的问题,在上篇博文中我们简单叙述了什么是 IOC。谈到控制反转就不得不提一下依赖注入了。在本次实验中,笔者通过往 Plugin2 插件注入一个简单服务来进一步理解。

首先,我们在 MefSample.Core 中创建一个服务接口 IService,示例代码如下所示

[Description("服务接口")]
public interface IService
{
    void QueryData(int numuber, Action action);
}

其次,我们在 MefSample.Service 中实现一个相应的服务 DataService,示例代码如下所示

[Description("具体服务")]
[Export(nameof(DataService),typeof(IService))]
public class DataService : IService
{
    public void QueryData(int numuber, Action action)
    {
        action(numuber * 100);
    }
}

最后,我们在 MefSample.Plugin2 的模块构造函数中注入一个服务 IService 类型的服务,示例代码如下所示

[Export(typeof(IView))]
[CustomExportMetadata(2, "Plugin 2", "这是第二个插件", "hippiezhou", "1.0")]
public partial class MainView : UserControl, IView
{
    public readonly IService Service;
    [ImportingConstructor]
    public MainView([Import("DataService")]IService service)
    {
        InitializeComponent();

        Service = service;
        Service.QueryData(10, sum => { this.tb.Text = sum.ToString(); });
    }
}

当我们重新编译程序项目并运行的话,会发现插件二模块的界面上会显示 1000。这里需要注意的是,你也可以不通过构造函数来注入服务,而是以属性的方式。但是我个人不建议这么做,因为它并不能很好的阐释了 DI 。这里建议读者朋友阅读一下 Artech 大叔发布的相关系列文章,确实相当精彩,很是值得阅读。

总结

MEF 对初学者来说可能不是很好理解,但是多写几个 Demo 试验几次就好了。如果后续还有时间的话,我会与大家简单分享一下 Prism 框架的使用。这里分享几个 Github 地址给大家

  • 本博客示例代码
  • CSharpDI.Notes
  • CSharpDI

当前文章:http://www.vmc-ahm.com/rxs/393331-679479-44190.html

发布时间:12:10:05

94404开奖直播??香港马会开奖结果??黄大仙玄机料001153期??抓码王??醉八仙论坛??香港90092九龙资料开奖??118kj手机看开奖??香港六合彩99876静心??白小一肖中特期期中??香港赛马会论坛??

[责任编辑: 平邓徒]

评论

?
[ 《CS:GO》迎中文语音:或聘甄子丹、周星驰御用配音 ]? [ 中国大陆GRE考生愈加青睐商科和物理科学相关专业 ]? [ 中兴回应“破产”:假消息,正加快公司恢复步伐 ]? [ 《地下城与勇士》还能再战!腾讯网络游戏2017年Q4收入 243.67亿元 ]? [ 境外媒体转发 《在全球贸易战下,“绥靖者”难逃被反噬的结局》 ]? [ 徐直军:2019年6月消费者即可用上华为5G手机 ]

?
  • 关于我们 | 蜘蛛资讯网 版权所有

    Copyright ? 2019 蜘蛛资讯网 All Rights Reserved