6

Ciao Sto usando un contenitore IoC e vorrei inizializzare un servizio (una parte del quale implica un "lavoro pesante" che comunica con un database) all'interno il costruttore.IoC Initialize Service con lavori pesanti nel costruttore ma evitando un metodo Init() temporale

Questo particolare servizio memorizza le informazioni che vengono rilevate da un servizio di iniezione IPluginToServiceProviderBridge, queste informazioni vengono salvate nel database tramite UnitOfWork.

Una volta eseguito il boot, i controller con comandi e servizi con gestori vengono utilizzati per tutte le altre interazioni. Tutti i comandi sono racchiusi all'interno di un ambito di vita, quindi il salvataggio e lo smaltimento di UnitOfWork vengono eseguiti dal gestore e non dal servizio (questo è ottimo per il codice pulito).

La stessa pulizia e separazione degli interessi per il risparmio e le transazioni non si applica per la Initializer all'interno del servizio come tutto si svolge nel costruttore:

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

Un paio di punti:

Idealmente mi voglio evitare di usare una fabbrica perché complica la gestione della vita dell'ottica, sarei felice di refactoring per una separazione migliore se sapessi come.

Voglio davvero evitare di avere un metodo separato Init() per il servizio, mentre consentirebbe la transazione e il salvataggio tramite comando/gestore, sarebbe richiesto un sacco di codice di controllo e credo che ciò introdurrebbe anche problemi temporali.

Considerato quanto sopra, è accettabile chiamare il UnitOfWork.Save() all'interno del mio costruttore o potrei refactoring per un codice più pulito e una migliore separazione?

+1

I concordano evitando il metodo Init(). odori di troppa responsabilità per un singolo oggetto. la fabbrica è la strada da percorrere qui. il concetto comunque. dovresti evitare questo tipo di lavoro nel ctor di un oggetto.se riesci a gestire l'ambito dell'oggetto, come può complicare una fabbrica? –

risposta

5

Lasciare che il costruttore del servizio faccia di più che archiviare le sue dipendenze in campi privati ​​è considerato una cattiva pratica quando si applica l'iniezione di dipendenza, poiché ciò consente di fallire la costruzione del grafo degli oggetti, rallenta la costruzione del grafico e complica il collaudo delle unità .

Quello che ho letto dalla tua domanda è che è necessario eseguire un po 'di inizializzazione all'avvio dell'applicazione. Va bene, dato che è abbastanza normale avere una fase di inizializzazione, ma non farlo all'interno di un costruttore. Basta spostare questa inizializzazione fino alla fine del codice di avvio dell'applicazione, dopo aver configurato il contenitore (e dopo aver eventualmente verificato la configurazione).

immagino il codice per assomigliare a questo:

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

si potrebbe anche scrivere un decoratore per il vostro PluginManagerService, ma questo potrebbe essere un po 'più di ingegneria, ma ... potrebbe essere simile a questo:

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

Questo decoratore può essere avvolto attorno ad un IPluginManagerService, e assicura che il sistema viene inizializzato appena prima della IPluginManagerService noi utilizzato per la prima volta, e assicura che soltanto inizializzato solo una volta. La logica di inizializzazione reale viene spostato in una classe separata (SRP), su cui il decoratore dipende:

public class PluginManagerServiceInitializer 
{ 
    private IUnitOfWork unitOfWork; 
    private IPluginToServiceProviderBridge serviceProvider; 

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

    public void Initialize() 
    { 
     var plugins = 
      from plugin in this.serviceProvider.GetAllPlugins() 
      select new PluginRecord 
      { 
       interfaceType = plugin.InterfaceType; 
       var id = plugin.Id; 
       var version = plugin.Version; 
      }; 

     unitOfWork.PluginsRepository.AddRange(plugins); 
    } 
} 
+0

Steven, grazie !! Spiega questi concetti così bene che vorrei poter invitare più volte =) per completezza di comprensione come si scriverà un decoratore per PluginManagerService? Questo avvolgerebbe qualsiasi comando al servizio o è qualcos'altro? – g18c

+0

@ g18c: vedere il mio aggiornamento. – Steven

Problemi correlati