2012-06-07 9 views
8

Ich würde gerne in der Lage sein, ninjects zu verwenden, um alle Instanzen eines bestimmten generischen Typs in eine Klasse zu injizieren. Zum Beispiel habe ich eine Reihe von benutzerdefinierten Extraktoren von einem Format ähnlich zu:Ninject Injection aller Instanzen eines generischen Typs mit Ninject

public interface IExtract<TEntity> 
{ 
    TEntity ExtractFrom(MyBulkExportedEntity exportedEntity); 
} 

und ich möchte alle Instanzen dieser Extraktoren in eine Klasse für die Bearbeitung dieser Datei mit ninject mehr Bindungs ​​verantwortlich injizieren.

dh

public class ProcessDataExtract 
{ 
    /*This isn't valid c# but demonstrates the intent of what i would like to do*/ 
    public ProcessDataExtract(IEnumerable<IExtract<>> allExtractors) 
    { 
    } 

    public void Process(MyBulkExportedEntity exportedEntity) 
    { 
     /*loop through all of the extractors and pull relevant data from the object*/ 
    } 
} 

In der Vergangenheit habe ich dies getan, indem eine Verwaltungsklasse mit (IProvideExtractors), die direkt den Kernel zugreift, aber Ich mag diese Methode nicht und wurde gefragt, ob jemand einen besseren Weg kennt um dies zu tun. Mit ninject mehrere Bindung Ich kann dann bekommen alle Instanzen kernel.GetAll(typeof(IExtract<>))

+1

Im Inneren des 'Process' Methode ist es erforderlich für die IEtract 'generisch sein? Wenn nicht, dann würde ich ein nicht generisches 'IExtract' erstellen und' IExtract 'würde von' IExtract' erben. Und mit der richtigen Registrierung in Ihrem 'ProcessDataExtract'-Konstruktor würden Sie sich auf' IEnumerable allExtractors 'verlassen. – nemesv

Antwort

4

Ich war auf der Suche nach etwas zu tun Related: Ich wollte nicht alle Bindungen separat mit der Konventionserweiterung angeben.

Zuerst: Sie müssen List<IExtract> injizieren und erben IExtract<T> : IExtract. Dies ist einfach aufgrund der Tatsache, dass Sie in C# nicht den Typ einer Sammlung mit verschiedenen Generika angeben können. Wie Sie in Ihrer Frage notiert haben, handelt es sich um eine ungültige Syntax - aus einem guten Grund, der über diese Antwort hinausgeht.

Sie können später die Elemente von IExtract aus der Liste ziehen und Reflektion verwenden, um den generischen Typ Paramer zu erhalten und ihn zurück zu werfen.Oder wenn Sie wissen, zu welchem ​​Extraktor Sie suchen:

public IExtract<T> GetExtractor<T>() { 
    return (IExtract<T>)Extractors.Find(e => e is ExtractImpl<T>); 
} 

Nun könnte man eine Reihe von Klassen für das haben Sie einige T zu IExtract` gebunden werden soll.

Bind<IExtract>().To<ExtractImpl<MyEntity>>(); 
Bind<IExtract>().To<ExtractImpl<YourEntity>>(); 

wo

MyEntity : BaseEntity 
YourEntity : BaseEntity 

Sie eine Konvention folgend angeben

Kernel.Bind(x => x.FromThisAssembly().SelectAllClasses() 
    .InheritedFrom<BaseEntity>() 
    .BindWith(new GenericArgumentBindingGenerator(typeof(IExtract<>)))); 

Wo GenericArgumentBindingGenerator ist defind wie:

public class GenericArgumentBindingGenerator : IBindingGenerator 
{ 
    private readonly Type m_Generic; 

    public GenericArgumentBindingGenerator(Type generic) 
    { 
     if (!generic.IsGenericTypeDefinition) 
     { 
      throw new ArgumentException("given type must be a generic type definition.", "generic"); 
     } 
     m_Generic = generic; 
    } 

    public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot) 
    { 
     if (type == null) 
      throw new ArgumentNullException("type"); 
     if (bindingRoot == null) 
      throw new ArgumentNullException("bindingRoot"); 
     if (type.IsAbstract || type.IsInterface) 
     { 
      return Enumerable.Empty<IBindingWhenInNamedWithOrOnSyntax<object>>(); 
     } 

     var bindings = new List<IBindingWhenInNamedWithOrOnSyntax<object>>(); 
     IBindingWhenInNamedWithOrOnSyntax<object> binding = bindingRoot 
      .Bind(typeof(IExtract)) // you maybe want to pass typeof(IExtract) to constructor 
      .To(m_Generic.MakeGenericType(type)); 

     bindings.Add(binding); 

     return bindings; 
    } 
} 
+0

Ich mag diesen Bindegenerator wirklich, es scheint ziemlich cool, auch ich denke, Sie haben Recht, von einem nicht-generischen zu verlängern würde dieses Problem in Bezug auf die Bindung lösen –

+0

Was ist IEnvironmentConfigFile es ersetzt werden? –

+0

Oh verdammt, ich habe einen verpasst - habe es aus meinem Code;) IEnvironmentConfigFile wäre IExtract ich denke. Aber es sollte durch den Konstruktor von GenericArgumentBindingGenerator übergeben werden, um es generisch zu machen. Sie möchten eigentlich Bind 'binden (). To >(); 'wie oben erwähnt. – Tarion

1

Option bei der Verwendung A

interessiert im, bin ich ziemlich sicher, dass Sie dies tun können:

public class ProcessDataExtract 
{ 
    public ProcessDataExtract<TExtract>(IEnumerable<IExtract<TExtract>> allExtractors) 
    { 
    } 
    ...etc... 
} 

Und dann heraus Liste Ihre Bindungen in Ihrer verbindlichen Modul Load Methode:

... 
Bind<IExtract<TEntity>>().To<SomeConcreteExtract>(); 
Bind<IExtract<TEntity>>().To<AnotherConcreteExtract>(); 
Bind<IExtract<TEntity>>().To<YetAnotherConcreteExtract>(); 
... 

und N Inject liefert sie an Ihren Konstruktor, der eine Abhängigkeit von einer Reihe von ihnen ankündigt. Das habe ich in der Vergangenheit mit Erfolg getan.

Option B

ändern

public interface IExtract<TEntity> 
{ 
    TEntity ExtractFrom(MyBulkExportedEntity exportedEntity); 
} 

zu

public interface IExtract 
{ 
    TEntity ExtractFrom<TEntity>(MyBulkExportedEntity exportedEntity); 
} 

die es erlauben würde:

 public ProcessDataExtract<TExtract>(IEnumerable<IExtract<TExtract>> allExtractors) 
    { 
    } 
    ...etc... 

sein:

public ProcessDataExtract(IEnumerable<IExtract> allExtractors) 
    { 
    } 
    ...etc... 

Und die ninject Bindungen eingestellt, auch würde:

... 
Bind<IExtract>().To<SomeConcreteExtract>(); 
Bind<IExtract>().To<AnotherConcreteExtract>(); 
Bind<IExtract>().To<YetAnotherConcreteExtract>(); 
... 
+0

Ja, in Ninject gibt es ein Konzept, alle Instanzen einer Bindung zu injizieren, was ich gerne nutzen würde. Sie können dies direkt von ninject mit der obigen Instanz tun, indem Sie 'kernel.GetAll (typeof (IExtract <>))' verwenden, was ein 'IEnumerable ' zurückgibt, das alle meine Extraktoren enthält. Mein Problem ist nicht mit diesem, mein Problem ist, dass ich nicht herausfinden kann, wie man dies in meinem Konstruktor spezifiziert, weil der obige Code nicht gültig ist C# –

+0

Ich korrigierte die Syntax. Sie werden jedoch mit nur einem Beton "TExtract" stecken bleiben. Dies müsste eine nicht-generische Basisklasse sein (abstrakt oder nicht). Oder Sie könnten den Typparameter von "IExtract" auf "ExtractFrom" schieben, wodurch die Homogenität von "ProcessDataExtract" entfernt würde. Ich füge hinzu, was ich meine Antwort meine. – bluevector

+0

Ich nehme an, was Ihr Sprichwort ist, könnte ich 'Enumerable > alleExtractors' verwenden, ich bin ziemlich sicher, dass dies nicht möglich ist, da ich denke' typeof (IExtract ) .IsAssignableFrom (Tyepof (IExtract )) 'siehe https: //compilify.net/1tj –

2

Die Antwort eine Möglichkeit, dies zu diesem scheint zu sein, dass es ist nicht mit ninject

+0

Es gibt einige Problemumgehungen in [Inject Array von Interfaces in Ninject] (http://stackoverflow.com/questions/3102940/inject-array-of-interfaces-in-ninject). – ladenedge

Verwandte Themen