簡體   English   中英

使用約定在MEF 2中使用默認構造函數和開放泛型

[英]Default constructor and open generics in MEF 2 using conventions

我正在嘗試在項目中使用MEF 2,通常使用SimpleInjector,但是這次我想嘗試MEF。 我的主要問題是處理開放式泛型,這就是我到目前為止所得到的

public interface ISetting {}
public class Setting : ISetting {}

public interface ILogger<TLog>
{
    TLog Fetch()
}

public class Logger<TLog> : ILogger<TLog>
{
    private ISetting settings;

    public Logger(ISetting settings)
    {
        this.settings = settings;
    }

    public TLog Fetch()
    {
        return default(TLog);
    }
}

現在我要做容器部分

var conventions = new ConventionBuilder();

conventions.ForType<Setting>()
           .Export<ISetting>()
           .Shared();

conventions.ForType(typeof(Logger<>))
           .Export(t => t.AsContractType(typeof(ILogger<>)))
           .Shared();

var configuration = new ContainerConfiguration()
        .WithAssembly(typeof(Program).Assembly,conventions);

using (var container = configuration.CreateContainer)
{
    var export = container.GetExport<ILogger<object>>(); //Exception :(
}

當嘗試檢索導出時,出現此異常

在類型'MEFTest.Logger`1 [System.Object]'上未找到導入構造函數。

如果我從Logger類中刪除構造函數,則容器構造封閉的泛型就可以了。 我有98%的把握是問題與構造函數有關,但我覺得這里遺漏了一些東西

編輯1:做一些閱讀,我實際上發現有2個版本的MEF,一個是Nuget軟件包,另一個是.NET40附帶的版本,我使用的是Nuget軟件包。 我進行了一些重構以使用.NET40附帶的組件

除了創建和使用容器的部分外,所有代碼都是相同的

var category = new AssemblyCatalog(Assembly.GetExecutingAssembly(), conventions);

using (var container = new CompositionContainer(category))
{
    var logic = container.GetExport<ILogger<int>>().Value; //Lazy Initialization O.o
    var test = logic.Fetch();

    // the rest of the code …
}

這有效:)很好,所以我肯定在Nuget軟件包的版本中缺少某些內容

編輯2:刪除了WithAssembly方法中通用零件的“自動檢測”功能后,代碼起作用了,這里是重構的代碼

約定部分:

var conventions = new ConventionBuilder();

conventions.ForType<Setting>()
           .Export<ISetting>();

conventions.ForType<Logger<int>>()
           .Export<ILogger<int>>();

容器部分:

var types = new Type[] { typeof(Setting), typeof(Logger<int>) };

var configuration = new ContainerConfiguration()
        .WithParts(types, conventions);

using (var container = configuration.CreateContainer())
{
    var logic = container.GetExport<ILogger<int>>();
    var test = logic.Fetch();

    // the rest of the code …
}

我將特定類型更改為integer 。當它執行Fetch()時,它會正確返回0作為int的默認值

有趣的部分是為什么泛型的“自動檢測”會強制標記構造函數

編輯3:我認為“自動檢測”部分不是這里的錯誤,因為我已經嘗試過

var conventions = new ConventionBuilder();

conventions.ForType<Setting>()
           .Export<ISetting>();

conventions.ForType(typeof(Logger<>))
           .Export(t => t.AsContractType(typeof(ILogger<>)));

var types = new Type[] { typeof(Setting), typeof(Logger<>) };

var configuration = new ContainerConfiguration()
        .WithParts(types, conventions);

using (var container = configuration.CreateContainer())
{
    var logic = container.GetExport<ILogger<int>>();
    var test = logic.Fetch();

    // the rest of the code …
}

有了該代碼,我回到正題,因為它會產生相同的異常,它會強制使用marking屬性

編輯4:實際的MEF項目已轉到System.Composition下的CoreFx GitHub頁面。 我去了單元測試,並在RegistrationBuilderCompatibilityTest行40-58

public interface IRepository<T> { }

public class EFRepository<T> : IRepository<T> { }

[Fact]
public void ConventionBuilderExportsOpenGenerics()
{
    var rb = new ConventionBuilder();

    rb.ForTypesDerivedFrom(typeof(IRepository<>))
      .Export(eb => eb.AsContractType(typeof(IRepository<>)));

    var c = new ContainerConfiguration()
        .WithPart(typeof(EFRepository<>), rb)
        .CreateContainer();

    var r = c.GetExport<IRepository<string>>();
}

沒有默認的構造函數,他們從未測試過它

結果:我最終在CoreFx Github頁面上打開了一個問題 ,並提交了帶有該錯誤修復程序的PR

嘗試使用[ImportingConstructor]屬性標記構造函數。

using System.Composition;
...
[ImportingConstructor]
public Logger(ISetting settings)
{
    this.settings = settings;
}

在他們合並我的PR並發布修正了錯誤的版本之前,我建議使用Edit 2上的解決方案。 我認為這是實現所需功能的最少介入方式

基本上,您需要使用CLOSED泛型類型構建約定,並使用這些封閉的泛型創建集合。 在容器中,使用WithParts方法以及已定義的約定 ,有關代碼段,請參見編輯2

更新

我的PR已合並,現在支持此功能。 它將在2017年4月30日到期的corefx 2.0中發布

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM