6

Hallo Ich benutze einen IoC Container und ich möchte einen Service initialisieren (ein Teil davon beinhaltet 'schwere Arbeit' im Gespräch mit einer Datenbank) innerhalb der Konstruktor.IoC Initialize Service mit Schwerarbeit im Konstruktor aber Vermeidung einer temporären Init() Methode

Dieser bestimmte Service speichert Informationen, die von einem injizierten IPluginToServiceProviderBridge Service gefunden werden, diese Informationen werden in der Datenbank über eine UnitOfWork gespeichert.

Sobald alles boot-bread ist, werden Controller mit Befehlen und Services mit Handlern für alle anderen Interaktionen verwendet. Alle Befehle sind innerhalb eines lebenslangen Gültigkeitsbereichs zusammengefasst, so dass das Speichern und Entsorgen des UnitOfWork durch den Handler und nicht durch den Dienst erfolgt (dies ist ideal für sauberen Code).

Die gleiche Ordentlichkeit und Trennung von Bedenken für das Speichern und Transaktionen nicht für die Initializer innerhalb des Dienstes gelten als alles Platz im Konstruktor:

public PluginManagerService(
    IPluginToServiceProviderBridge serviceProvider, 
    IUnitOfWork unitOfWork) 
{  
    this.unitOfWork = unitOfWork; 
    this.serviceProvider = serviceProvider; 

    lock (threadLock) 
    { 
     if (initialised == false) 
     { 
      LinkPluginsWithDatabase(); 
      initialised = true; 
     } 

     // I don't like this next line, but 
     // not sure what else to do 
     this.UnitOfWork.Save(); 
    } 
} 

protected void LinkPluginsWithDatabase() 
{ 
    var plugins = 
     this.serviceProvider.GetAllPlugins(); 

    foreach (var plugin in plugins) 
    { 
     var db = new PluginRecord 
     { 
      interfaceType = plugin.InterfaceType; 
      var id = plugin.Id; 
      var version = plugin.Version; 
     } 
     // store in db via unit of work repository 
     this.unitOfWork.PluginsRepository.Add(db); 
    } 
} 

Ein paar Punkte:

Ideale I Ich möchte vermeiden, eine Fabrik zu verwenden, da es die Handhabung der Lebensdauer des Oszilloskops erschwert, und würde mich freuen, für eine bessere Trennung zu refaktorieren, wenn ich wüsste, wie.

Ich möchte wirklich vermeiden, eine separate Init() Methode für den Service zu haben, während es Transaktion und Speichern über Befehl/Handler erlauben würde, viel Prüfcode wäre erforderlich und ich glaube, dass dies auch zeitliche Probleme einführen würde.

Angesichts der oben genannten, ist es akzeptabel, UnitOfWork.Save() innerhalb meines Konstruktors anrufen oder könnte ich für sauberer Code und bessere Trennung umgestalten?

+1

Ich stimme der Vermeidung der Init() -Methode zu. riecht nach zu viel Verantwortung für ein einzelnes Objekt. Fabrik ist der Weg, hier zu gehen. das Konzept sowieso. Sie sollten diese Art von Arbeit in der Ctor eines Objekts vermeiden.Wenn Sie den Umfang des Objekts verwalten können, wie kompliziert eine Fabrik das? –

Antwort

5

Das Konstruieren von Konstrukteuren bedeutet mehr als nur das Speichern von Abhängigkeiten in privaten Feldern. Dies ist eine schlechte Methode beim Anwenden der Abhängigkeitsinjektion, da dies die Konstruktion des Objektgraphen zum Scheitern bringt, die Erstellung des Diagramms verlangsamt und Komponententests erschwert .

Was ich von Ihrer Frage gelesen habe, ist, dass Sie einige Initialisierung durchführen müssen, wenn die Anwendung gestartet wird. Das ist in Ordnung, da es eine normale Initialisierungsphase ist, aber nicht innerhalb eines Konstruktors. Verschieben Sie diese Initialisierung an das Ende des Anwendungsstartcodes, nachdem Sie den Container konfiguriert haben (und nachdem Sie Ihre Konfiguration optional überprüft haben).

Ich stelle mir Ihren Code wie folgt aussehen:

public void Application_Start(object s, EventArgs e) 
{ 
    Container container = new Container(); 

    Bootstrap(container); 

    InitializeApplication(container); 
} 

private void InitializeApplication(
    Container container) 
{ 
    using (this.container.BeginLifetimeScope()) 
    { 
     var pluginManager = this.container 
      .GetInstance<PluginManagerService>(); 

     pluginManager.LinkPluginsWithDatabase(); 

     var unitOfWork = 
      container.GetInstance<IUnitOfWork>(); 

     unitOfWork.Save(); 
    } 
} 

Sie könnten sogar einen Dekorateur für Ihre PluginManagerService schreiben, aber dies könnte ein wenig vorbei sein entworfen, aber ... es könnte wie folgt aussehen:

public class InitializingPluginManagerServiceDecorator 
    : IPluginManagerService 
{ 
    private static readonly object syncRoot = new object(); 
    private static bool initialized; 

    private IPluginManagerService decorated; 
    private Container container; 

    public InitializingPluginManagerServiceDecorator(
     IPluginManagerService decorated, 
     Container container, 
     IPluginToServiceProviderBridge serviceProvider) 
    { 
     this.pluginManagerService = pluginManagerService; 
     this.container = container; 
     this.serviceProvider = serviceProvider; 
    } 

    public void PluginManagerServiceMethod() 
    { 
     this.InitializeInLock();   

     this.decorated.PluginManagerServiceMethod(); 
    } 

    private void InitializeInLock() 
    { 
     if (!initialized) 
     { 
      lock (syncRoot) 
      { 
       if (!initialized) 
       { 
        this.InitializeInScope(); 
       } 
      } 

      initialized = true;  
     } 
    } 

    private void InitializeInScope() 
    { 
     using (this.container.BeginLifetimeScope()) 
     { 
      this.InitializeWithSave(); 
     } 
    } 

    private void InitializeWithSave() 
    { 
     var uow = 
      this.container.GetInstance<IUnitOfWork>() 

     var initializer = this.container 
      .GetInstance<PluginManagerServiceInitializer>(); 

     initializer.Initialize(); 

     uow.Save();  
    } 
} 

Dieser Dekorateur ist um eine IPluginManagerService gewickelt werden, und sorgt dafür, dass das System kurz vor dem IPluginManagerService initialisiert wird uns zum ersten Mal verwendet wird, und sorgt dafür, dass es erst einmal initialisiert. Die eigentliche Initialisierungslogik wird in eine separate Klasse (SRP) verschoben, von der der Dekorator abhängig ist:

+0

Steven, danke !! Du erklärst diese Konzepte so gut, ich wünschte, ich könnte mehrmal upvote =) für die Vollständigkeit des Verständnisses wie würde man einen Decorator für PluginManagerService schreiben? Würden damit irgendwelche Befehlsaufrufe an den Dienst übertragen oder ist es etwas anderes? – g18c

+0

@ g18c: Siehe mein Update. – Steven