2016-07-20 10 views
3

Wie registriere ich Typen, die einen anderen registrierten Typ als Parameter und auch einfache Typen (wie eine Ganzzahl) annehmen?SimpleInjector ctor injection mix registrierte Typen und einfache Werte

public interface IDeviceManager 
{ 
    // implementation omitted. 
} 

public class DeviceManager : IDeviceManager 
{ 
    public DeviceManager(IDeviceConfigRepository configRepo, int cacheTimeout) 
    { 
     // implementation omitted 
    } 
} 

ich für die IDeviceConfigRepository einen Container Registrierung haben. Das ist ok. Aber wie erstelle ich eine Instanz von DeviceManager mit der konfigurierten Abhängigkeit und übergebe eine Ganzzahl meiner Wahl im Kompositionswurzel?

Ich dachte über die Schaffung einer Fabrik nach.

public class DeviceManagerFactory : IDeviceManagerFactory 
{ 
    private readonly Container _container; 

    public DeviceManagerFactory(Container container) 
    { 
     _container = container; 
    } 

    public DeviceManager Create(int minutes) 
    { 
     var configRepo = _container.GetInstance<IDeviceConfigurationRepository>(); 
     return new DeviceManager(configRepo, minutes); 
    } 
} 

Das ist ziemlich einfach.
Allerdings habe ich jetzt keine Registrierung für DeviceManager welches ist der Typ, den ich letztlich brauche. Soll ich diese Abhängigkeiten stattdessen zur Fabrik wechseln?

public class ExampleClassUsingDeviceManager 
{ 
    private readonly DeviceManager _deviceManager; 

    public ExampleClassUsingDeviceManager(DeviceManager deviceManager, ...) 
    { 
     _deviceManage = deviceManager; 
    } 

    // actions... 
} 

Damit dies funktioniert und zirkuläre Abhängigkeiten zu vermeiden, würde ich wahrscheinlich das Werk von dem „Anwendung“ Projekt verschieben muß (im Gegensatz zu Klassenbibliotheken gegen), wo die Zusammensetzung Wurzel zu dem Projekt ist, wo die Devicemanager implementiert werden .

Ist das OK? Es würde natürlich bedeuten, um den Container herumzugehen.

Irgendwelche anderen Lösungen zu diesem?

EDIT Im gleichen Projekt für andere Arten Ich bin mit parameter objects Konfiguration in mein Objektgraphen zu injizieren. Das funktioniert OK, da ich nur eine Klasseninstanz pro Parameterobjekttyp habe. Wenn ich verschiedene Parameterobjektinstanzen (zB MongoDbRepositoryOptions) in verschiedene Klasseninstanzen einfügen müsste (zum Beispiel MongoDbRepository), müsste ich irgendeine Art von benannter Registrierung verwenden - was SimpleInjector nicht unterstützt. Obwohl ich nur eine ganze Zahl habe, würde das Parameter Objektmuster mein Problem lösen. Aber ich bin nicht allzu glücklich über dieses Muster und weiß, dass es bricht, sobald ich mehrere Instanzen der konsumierenden Klasse (d. H. MongoDbRepository) habe.

Beispiel:

MongoDbRepositoryOptions options = new MongoDbRepositoryOptions(); 
MongoDbRepositoryOptions.CollectionName = "config"; 
MongoDbRepositoryOptions.ConnectionString = "mongodb://localhost:27017"; 
MongoDbRepositoryOptions.DatabaseName = "dev"; 

container.RegisterSingleton<MongoDbRepositoryOptions>(options); 
container.RegisterSingleton<IDeviceConfigurationRepository, MongoDbRepository>(); 

Ich bin gespannt zu hören, wie Sie mit Konfigurationen am besten umgehen Kompositions Wurzel getan.

+1

Ich empfehle Steven (Autor von SimpleInjector) ausgezeichneten Artikel über die Injektion von Laufzeitdaten zu lesen: https://cuttingedge.it/blogs/steven/pivot/entry.php?id=99 –

Antwort

0

Lassen Sie Ihre DeviceManagerFactory von Container abhängen ist in Ordnung, solange diese Fabrik Implementierung Teil Ihrer Composition Root ist.

Eine weitere Option ist die IDeviceConfigRepository in die DeviceManagerFactory, auf diese Weise injizieren Sie eine DeviceManager ohne die Notwendigkeit konstruieren können die Behälter zuzugreifen:

public class DeviceManagerFactory : IDeviceManagerFactory { 
    private readonly IDeviceConfigurationRepository _repository; 

    public DeviceManagerFactory(IDeviceConfigurationRepository repository) { 
     _repository = repository; 
    } 

    public DeviceManager Create(int minutes) { 
     return new DeviceManager(_repository, minutes); 
    } 
} 

aber jetzt habe ich nicht eine Registrierung für Device die ist der Typ, den ich letztendlich brauche. Soll ich diese Abhängigkeiten stattdessen zur Fabrik wechseln?

Im Allgemeinen würde ich sagen, dass Fabriken in der Regel die falsche Abstraktion sind, da sie den Verbraucher komplizieren, anstatt sie zu vereinfachen. Daher sollten Sie in der Regel auf die Service-Abstraktion selbst angewiesen sein (anstatt von einer Factory-Abstraktion abhängig zu sein, die Service-Abstraktionsimplementierungen erzeugen kann), oder Sie sollten eine Art Proxy oder Mediator einfügen, der die Existenz der Service-Abstraktion aus Sicht der Kunden komplett verbirgt der Verbraucher.

@DavidL zeigt auf my blog post about runtime data. Ich bin nicht sicher, ob die cacheTimeout Laufzeitdaten ist, obwohl Sie scheinen, es als solche zu verwenden, da Sie es in die Create Methode der Fabrik übergeben. Aber wir verpassen hier einen Zusammenhang, um festzustellen, was vor sich geht. obwohl meine Blog-Post steht nach wie vor, wenn es Laufzeitdaten, es ist ein Anti-Muster und in diesem Fall sollten Sie

  1. Pässe Laufzeitdaten über Methodenaufrufe der API

oder

  1. rufen Sie Laufzeitdaten aus bestimmten Abstraktionen ab, die das Auflösen von Laufzeitdaten ermöglichen.

UPDATE

Falls der Wert, den Sie verwenden eine Anwendung konstant ist, dass durch die Konfigurationsdatei gelesen wird, und ändert sich nicht während der Lebensdauer der Anwendung, ist es völlig in Ordnung, um es durch den Konstruktor zu injizieren. In diesem Fall ist es nicht ein Laufzeitwert. Es gibt auch keine Notwendigkeit für eine Fabrik.

Es gibt mehr Möglichkeiten, dies in der Einfachen Injector zu registrieren, zum Beispiel Sie einen Delegaten die DeviceManager Klasse verwenden können, um registrieren:

container.Register<DeviceManager>(() => new DeviceManager(
    container.GetInstance<IDeviceConfigRepository>(), 
    cacheTimeout: 15)); 

Nachteil dieses Ansatzes ist, dass Sie die Fähigkeit des einfachen Injector an Auto verlieren -wire den Typ für Sie und deaktivieren Sie die Fähigkeit des Simple Injector, das Objektdiagramm für Sie zu verifizieren, zu diagnostizieren und zu visualisieren. Manchmal ist das in Ordnung, manchmal nicht.

Das Problem hier ist, dass Simple Injector die Registrierung von primitiven Typen blockiert (weil sie Mehrdeutigkeit verursachen), während Sie nicht mit einem sauberen Weg zur Registrierung zu präsentieren. Wir überlegen, (endlich) ein solches Feature in Version 4 hinzuzufügen, aber das entspricht nicht wirklich Ihren aktuellen Bedürfnissen.

Mit dem einfachen Injektor können Sie nicht einfach eine primitive Abhängigkeit angeben, während der Container den Rest automatisch verdrahtet. Die IDependencyInjectionBehavior Abstraktion des einfachen Injektors erlaubt es Ihnen, das Standardverhalten zu überschreiben (was dies verbietet). Dies wird beschrieben here, aber ich rate normalerweise davon ab, dies zu tun, weil es in der Regel ziemlich viel Code erfordert.

Grundsätzlich gibt es zwei Möglichkeiten:

  1. Zusammenfassung die spezifische Logik, die aus der Klasse mit diesem Caching behandelt und es in einer neuen Klasse wickeln. Diese Klasse wird nur die cacheTimeout als ihre Abhängigkeit haben.Dies ist natürlich nur nützlich, wenn es tatsächlich logisch ist, zu abstrahieren, und ist normalerweise nur logisch, wenn Sie diesen primitiven Wert in mehrere Konsumenten einspeisen. Zum Beispiel, anstatt eine connectionstring in mehrere Klassen zu injizieren, ist es wahrscheinlich besser, statt dessen eine IConnectionFactory in diese Klassen zu injizieren.
  2. Umschließen Sie den Wert cacheTimeout in einen komplexen Datencontainer, der für die verbrauchende Klasse spezifisch ist. Dies ermöglicht Ihnen, diesen Typ zu registrieren, da er das Mehrdeutigkeits-Problem behebt. In der Tat, das ist, was Sie selbst vorschlagen, und ich denke, das ist eine wirklich gute Sache zu tun. Da diese Werte zur Laufzeit konstant sind, ist es in Ordnung, diesen DTO als Singleton zu registrieren, aber stellen Sie sicher, dass er unwandelbar ist. Wenn Sie jedem Kunden ein eigenes Datenobjekt geben, müssen Sie nicht mehrere Instanzen davon registrieren, da diese eindeutig sind. Btw, obwohl benannte Registrierungen nicht unterstützt werden, können Sie konditionale oder kontextuelle Registrierungen unter Verwendung von RegisterConditional machen und es gibt andere Möglichkeiten, benannte Registrierungen mit Simple Injector zu erreichen, aber ich glaube nicht, dass Sie das hier wirklich brauchen.
+0

Ich bin nicht sicher, ob Ich verstehe "Laufzeitdaten" richtig. Dies sind keine Informationen, die für ein Gerät oder eine Anforderung individuell sind oder für einen anderen Bereich als für die Anwendungslebensdauer. Ich übergebe eine Integer (Minuten) an den DeviceManager ctor, um die interne Caching-Strategie des DeviceManagers zu konfigurieren (es ist ein wirklich einfacher Mechanismus - nichts, was ich in seine eigenen injizierbaren Bits umgestalten möchte (wie ICacheAdapter) ;-) Sollte ich vielleicht einen IConfigurationManager für die Objekte in meinem Objektdiagramm einfügen, damit sie sich selbst konfigurieren können? – lapsus

+0

@lapsus, was Sie sagen ist, dass dieses 'cacheTimeout' während der Lebensdauer der Anwendung konstant ist (zB ein Konfigurationswert) "Sie haben nur einen Wert für" cacheTimeout "während der Laufzeit der Anwendung? In diesem Fall würde meine Antwort anders sein. – Steven

+0

Ja. Der DeviceManager wird eine Singleton-Instanz mit Gültigkeitsdauer des Anwendungsbereichs sein. Ich dachte, den Wert unter Verwendung des Konstruktors zu setzen eine gute Idee (so könnte ich es am Kompositionswurzel definieren.) Sag mir nicht, ich solle eine öffentliche const Art Lösung verwenden ;-) – lapsus