MEF 插件式開發之 DotNetCore 初體驗


背景敘述

在傳統的基於 .Net Framework 框架下進行的 MEF 開發,大多是使用 MEF 1,對應的命名空間是 System.ComponentModel.Composition。在 DotNet Core 中,微軟為了偉大的跨平台策略,引入了 MEF 2,其對應的命名空間是 System.Composition,這個需要開發者自己在 Nuget 上進行下載安裝 Microsoft.Composition。2 與 1 相比,無論是在支持平台上還是性能上都有改進,值得我們探討一下。

動手實驗

實驗1:在 DotNetCore 控制台程序中嘗試使用 MEF2

首先,我們創建一個 DotNet Core 控制台應用程序,然后為其添加 MEF2 對應的 Package:Microsoft.Composition;

然后,我們創建一個示例接口:

public interface IMessageSender
{
    void Send(string message);
}

接着,我們再創建一個示例類來實現該接口,並嘗試將其導出:

[Export(typeof(IMessageSender))]
public class EmailSender : IMessageSender
{
    public void Send(string message)
    {
        Console.WriteLine(message);
    }
}

最后,我們在主程序中進行調用:

class Program
{
    static void Main(string[] args)
    {
        //尋找主程序命名空間
        var assembiles = new[] { typeof(Program).GetTypeInfo().Assembly };

        //配置 MEF 容器
        var configuration = new ContainerConfiguration()
            .WithAssembly(typeof(Program).GetTypeInfo().Assembly);
        using (var container = configuration.CreateContainer())
        {
            //依據相應接口獲取導出的具體類
            IMessageSender messageSender = container.GetExport<IMessageSender>();

            messageSender.Send("Hello MEF2");
        }
        Console.ReadKey();
    }
}

此時,如果一切正常的話,程序會輸入如下結果:

實驗2:在 DotNetCore 控制台程序中嘗試使用 MEF2 加載外部組件

由於微軟在 DotNetCore 中為開發者提供了新的程序集加載方式 AssemblyLoadContext。它允許多次加載相同的程序集,並創建相互獨立的副本,並且它比 AppDomain 重量輕得多。因此我在本次實驗中,筆者嘗試使用這種新的加載方式進行實驗。

首先,我們創建一個如下圖所示的解決方案:

  • DotNetCoreMEF:控制台程序,安裝 Microsoft.Composition,並引用 DotNetCoreMEF.Core
  • DotNetCoreMEF.Core:核心類庫,用於定義相關接口;
  • DotNetCoreMEF.Plugin1:插件類庫,安裝 Microsoft.Composition,並引用 DotNetCoreMEF.Core
  • DotNetCoreMEF.Plugin2:插件類庫,安裝 Microsoft.Composition,並引用 DotNetCoreMEF.Core

注意:請確保上述項目的生成目錄保持一致。

相關示例代碼如下所示:

IMessageSender.cs

public interface IMessageSender
{
    void Send(string message);
}

EmailSender.cs

[Export(typeof(IMessageSender))]
public class EmailSender : IMessageSender
{
    public void Send(string message)
    {
        Console.WriteLine($"Email:{message}");
    }
}

SMSSender.cs

[Export(typeof(IMessageSender))]
public class SMSSender : IMessageSender
{
    public void Send(string message)
    {
        Console.WriteLine($"SMS:{message}");
    }
}

Program.cs

class Program
{
    static void Main(string[] args)
    {
        var assembiles = Directory.GetFiles(AppContext.BaseDirectory, "*.dll", SearchOption.TopDirectoryOnly)
            .Select(AssemblyLoadContext.Default.LoadFromAssemblyPath);

        var conventions = new ConventionBuilder();
        conventions.ForTypesDerivedFrom<IMessageSender>()
            .Export<IMessageSender>()
            .Shared();

        var configuration = new ContainerConfiguration()
            .WithAssemblies(assembiles, conventions);

        using (var container = configuration.CreateContainer())
        {
            IEnumerable<IMessageSender> senders = container.GetExports<IMessageSender>();
            foreach (var sender in senders)
            {
                sender.Send("Hello World");
            }
        }

        Console.ReadKey();
    }
}

此時,我們將項目全部重新編譯一下,可通過 VS 調試運行,看到相應的輸出結果。當然,我們也可以通過命令行的方式運行程序,前提是我們需要將我們的程序發布一下。發布好后我們可以執行 dotnet DotNetCoreMEF.dll 看到輸出結果:

總結

上述展示的只是 MEF 在 DotNet Core 中的簡單應用,其中需要注意的是 AssemblyLoadContext ,此外,關於模塊的 延遲記載元數據的獲取 ,感興趣的朋友可參考我之前的一篇博客進行參考:MEF 插件式開發 - WPF 初體驗

其實,如果對 DotNet Core 有一定了解的朋友是知道的,上述這種方式雖然實現了插件式的開發模式,但是並沒有完全發揮 DotNet Core 本身所具有優勢:內置 DI。所以,我們完全可以使用更高效的方式來實現。在下篇博客中,我們將感受一下 DotNet Core 中強大的 DI 。

相關參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM