2016-08-05 42 views
0

Ich versuche, MEF 2 in meinem Projekt zu verwenden, verwende ich normalerweise SimpleInjector, aber dieses Mal wollte ich MEF versuchen. Mein Hauptproblem ist, mit offenen Generika tun haben, ist es das, was ichStandardkonstruktor und Open Generics in MEF 2 mit Konventionen

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); 
    } 
} 

Jetzt

für den Behälterteil
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 :(
} 

ich so weit gekommen, wenn es versucht, den Export abrufen Ich erhalte diese Ausnahme

Es wurde kein importierender Konstruktor für den Typ 'MEFTest.Logger`1 [System.Object]' gefunden.

Wenn ich den Konstruktor aus der Logger-Klasse entfernen, konstruieren die Container die geschlossenen generischen einfach gut. Ich bin 98% sicher, dass das Problem mit den Konstrukteuren verwandt ist, aber ich glaube, ich etwas bin hier fehlt

Edit 1: etwas zu lesen Doing ich tatsächlich entdeckt haben, dass es 2 Versionen von MEF, eine, die eine Nuget ist Paket und ein anderes, das mit .NET40 geliefert wird, ist das Nuget-Paket, das ich verwende. Ich habe einige Refactoring die man zu verwenden, die Schiffe mit .NET40

den Code Alles ist gleich, außer für den Teil, der den Container erstellt und verwenden

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 … 
} 

Das funktioniert :) ganz gut so auf jeden Fall ich bin etwas in der Version des Pakets Nuget fehlt

Edit 2: Mit der Entfernung der "auto-detection" der generischen Teile im WithAssembly Methode der Code funktioniert, ist hier t Code er refactored

Die Konvention Teil:

var conventions = new ConventionBuilder(); 

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

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

Der Teil Behälter:

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 … 
} 

I die spezifische Art zu ganzen Zahl verändert.Wenn er ausgeführt wird Fetch() es für int korrekt 0 als Standardwert zurückgibt

Der interessante Teil ist, warum die „auto-detection“ der Generika der Konstruktor zwingen

bearbeiten markiert 3: ich denke, der „auto-detection“ Teil hier nicht die Schuld ist, weil ich dies mit zu squar diesem Code

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 … 
} 

versucht habe, ich bin zurück e ein, weil sie die gleiche Ausnahme erzeugt, erzwingt sie die Verwendung des Markierungs Attribut

Bearbeiten 4: Die eigentliche MEF-Projekt auf die Seite unter System.CompositionCoreFx GitHub gegangen ist. Ich ging zu den Unit-Tests und in den RegistrationBuilderCompatibilityTest Linien 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>>(); 
} 

Sie nie ohne den Standard-Konstruktor getestet

Ergebnisse: ich am Ende ein issue auf der CoreFx Github Seite öffnen und übermittelte eine PR mit einer Fehlerbehebung für den Fehler

+0

Ich habe schon versucht 'SelectConstructor (ctr => ctr.FirstOrDefault())' und ist ein Nein. ** BTW ** Wenn ich die Generika von all dem entferne, funktioniert es – TMiNus

Antwort

0

Bis sie verschmelzen meine PR und setzen eine Version mit dem Bug behoben ich mit der Lösung auf Edit 2 empfehlen würde. Ich denke, es ist die am wenigsten intrusive Art, die erforderliche Funktionalität zu erreichen

Grundsätzlich müssen Sie Konventionen mit Ihren GESCHLOSSENEN generischen Typen erstellen und eine Sammlung mit diesen geschlossenen Generika erstellen. In dem Behälter verwenden, um die WithParts Methode zusammen mit den Konventionen, die definiert wurden, Edit 2 für Code-Schnipsel

UPDATE

Mein PR wurde verschmolzen und diese Funktionalität wird nun unterstützt sehen. Es wird in corefx 2.0 fällig bis 30. April 2017

1

Versuchen Sie, den Konstruktor mit dem Attribut [ImportingConstructor] zu markieren.

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

Ich möchte die Verwendung von Markups vermeiden, denn wenn ich diesen Code in eine Bibliothek lege, gehe ich davon aus, dass der Client MEF verwenden wird, um die Klassen zu injizieren. Außerdem ist etwas sogar mit dem Markup aus, denn wenn ich den Konstruktor entferne, wird MEF auch ohne Markierung den Standard verwenden. Ich weiß nicht, ob ich es klar genug erklären – TMiNus

+0

Ihre Lösung funktioniert, nach diesem Artikel https://msdn.microsoft.com/en-us/library/ee155691(v=vs.110).aspx in den ** Prerequisite Imports ** besagt, dass MEF den Standardkonstruktor auswählt und einen Fehler auslöst, wenn [ImportingConstructor] nicht definiert ist oder mehrere [ImportingConstructor] definiert sind, aber wie gesagt, dies sollte ohne Konventionen möglich sein – TMiNus