2012-06-22 18 views
6

Hola, estoy usando un contenedor IoC y me gustaría inicializar un servicio (parte del cual implica "trabajo pesado" hablando con una base de datos) el constructorIoC Initialize Service con trabajo pesado en constructor pero evitando un método Init() temporal

Este servicio particular almacena información que se encuentra en un servicio IPluginToServiceProviderBridge inyectado, esta información se guarda en la base de datos a través de UnitOfWork.

Una vez que todo está bien ajustado, los controladores con comandos y los servicios con controladores se usan para cualquier otra interacción. Todos los comandos se envuelven dentro de un alcance de por vida, por lo que guardar y deshacerse de UnitOfWork es hecho por el manejador y no por el servicio (esto es ideal para código limpio).

La misma pulcritud y la separación de las preocupaciones para el ahorro y las transacciones no se aplica para el Initializer dentro del servicio, ya que todo se lleva a cabo en el constructor:

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 par de puntos:

Lo ideal quiero evitar el uso de una fábrica, ya que complica el manejo de la vida útil del osciloscopio, me gustaría refactorizar para una mejor separación si supiera cómo.

Realmente quiero evitar tener un método Init() separado para el servicio, mientras que permitiría la transacción y el guardado a través del comando/controlador, se requerirían muchos códigos de comprobación y creo que esto también introduciría problemas temporales.

Dado lo anterior, ¿es aceptable llamar al UnitOfWork.Save() dentro de mi constructor o podría refactorizar para obtener un código más limpio y una mejor separación?

+1

Acepto evitar el método Init(). huele a demasiada responsabilidad por un solo objeto. fábrica es el camino a seguir aquí. el concepto de todos modos. deberías evitar este tipo de trabajo en el ctor de un objeto.si puede gestionar el alcance del objeto, ¿cómo lo complica una fábrica? –

Respuesta

5

Dejar que el constructor de su servicio haga algo más que almacenar sus dependencias en campos privados es considerado una mala práctica al aplicar la inyección de dependencia, ya que esto permite la construcción del gráfico de objetos, ralentiza la construcción del gráfico y complica las pruebas unitarias .

Lo que leí de su pregunta es que necesita hacer algunas inicializaciones cuando se inicia la aplicación. Está bien, ya que es bastante normal tener alguna fase de inicialización, pero no hagas esto dentro de un constructor. Simplemente mueva esta inicialización al final del código de inicio de la aplicación, después de configurar el contenedor (y después de que haya verificado su configuración de forma opcional).

me imagino su código para tener este aspecto:

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

incluso se podría escribir un decorador para su PluginManagerService, pero esto podría ser un poco más de ingeniería, pero ... podría tener este aspecto:

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

Este decorador se puede envolver alrededor de un IPluginManagerService, y asegura que el sistema se inicia justo antes de la IPluginManagerService nosotros utilizamos por primera vez, y asegura que sólo se inicializa una sola vez. La lógica de inicialización real se mueve a una clase separada (SRP), de la que depende el decorador:

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, ¡gracias! Usted explica estos conceptos tan bien que desearía poder votar más veces =) para completar la comprensión de cómo se escribiría un decorador para PluginManagerService? ¿Esto envolvería cualquier llamada de comando al servicio o es algo más? – g18c

+0

@ g18c: ver mi actualización. – Steven

Cuestiones relacionadas