2012-08-16 10 views
13

Mi aplicación incluye una serie de conjuntos de servicios de fondo (incluida una capa de repositorio de datos de Entity Framework) que son compartidos por una serie de ensamblajes frontales (incluido un servicio de Windows y una aplicación web MVC3).Dónde ubicar los módulos Ninject en una aplicación de varios niveles

Mi comprensión del proceso de enlace de Ninject es que cada conjunto que contiene tipos inyectables también debe contener un módulo Ninject que defina los enlaces predeterminados para estos tipos. El conjunto de módulos definidos se cargaría en el Kernel de Ninject de los ensamblados consumidores.

Sin embargo, estoy teniendo problemas, ya que el ámbito de enlace obligatorio no siempre es coherente. Por ejemplo, mi proyecto MVC debe vincularse al contexto de datos InRequestScope, mientras que el servicio de Windows se une a la misma clase InThreadScope.

Obviamente, puedo resolver este problema reubicando todos los módulos en los proyectos frontales y mantener copias separadas de cada módulo para cada escenario de uso, pero esto parece raro, ya que duplica gran parte del contenido del módulo en múltiples proyectos .

¿Existe una mejor práctica acerca de dónde deben ubicarse los módulos en una aplicación de varios niveles y cómo puedo conciliar esto con mi necesidad de diferencias vinculantes entre proyectos?

Muchas gracias por sus sugerencias,

Tim

+0

ver también http://stackoverflow.com/questions/1699197/how-do-you-organise-your-ninject-modules (IIRC esta Q es una dup, pero esta es la mejor que tengo por ahora) –

+0

Gracias, Rubén. Tienes razón en que hay mucho en común entre estas dos preguntas. Me gusta especialmente su sugerencia de pasar parámetros de tiempo de ejecución a módulos que residen en ensamblajes compatibles, muy flexible. –

+0

Hmm; eso es hace un tiempo (no estaba intentando adular mi respuesta de ninguna manera). Es posible que haya significado literalmente * pasar parámetros * en el día - en general trataría de hacerlo a través de interfaces tanto como sea posible. Además, eso fue antes de http://manning.com/seemann, lo que reduce la cantidad de preguntas que encontrará intrigante en la arquitectura DI: def run comprátelas sin hacer preguntas. –

Respuesta

12

Para una solución con una sola aplicación, el consejo general es registrar su contenedor en el proyecto de la aplicación (su aplicación web o proyecto de servicio web). Para una aplicación web, esto sería generalmente Global.asax Application_Start. Este lugar donde se conecta todo junto se llama Composition Root en terminología DI.

Con una solución de aplicaciones múltiples, aún tendría una sola raíz de composición por proyecto de aplicación. Esto tiene que ser así, ya que cada aplicación tiene su configuración única. Por otro lado, el código duplicado siempre es malo. No desea tener que cambiar tres lugares cuando introduce una nueva abstracción.

El truco es mover todos los registros a la jerarquía del proyecto. Por ejemplo, puede definir un único "conjunto de arranque" que dependa de los ensamblajes de su capa de negocio (y debajo) y deje que todos los registros de esos ensamblajes no cambien.Las raíces de composición de las aplicaciones pueden usar ese ensamblaje para obtener los registros predeterminados y ampliarlo con las dependencias específicas de la aplicación.

Tal cosa podría tener este aspecto:

// MVC Composition root 
public static void Bootstrap() 
{ 
    var container = new Container(); 

    // Default registrations 
    BusinessLayerBootstrapper.Bootstrap(container); 

    // Application specific registrations 
    container.Bind<IUserContext>().To<AspNetUserContext>(); 

    DependencyResolver.Current = 
     new ContainerDependencyResolver(container); 
} 

// Windows Service Composition root 
public static void Bootstrap() 
{ 
    var container = new Container(); 

    // Default registrations 
    BusinessLayerBootstrapper.Bootstrap(container); 

    // Application specific registrations 
    container.Bind<IUserContext>().To<SystemUserContext>() 
     .SingleScoped(); 

    // Store somewhere. 
    Bootstrapper.Container = container; 
} 

// In the BL bootstrap assembly 
public static class BusinessLayerBootstrapper 
{ 
    public static void Bootstrap(Container container) 
    { 
     container.Bind<IDepenency>().To<RealThing>(); 
     // etc 
    } 
} 

Aunque usted no necesita tener un programa previo de montaje separado (se puede colocar este código en el propio BL), esto le permite mantener su negocio conjuntos de capas libres de cualquier dependencia a su contenedor.

También tenga en cuenta que estoy llamando a un método estático Bootstrap(), en lugar de utilizar (Ninject) Módulos. Traté de mantener mi respuesta independiente del marco, ya que tu pregunta es general y el consejo será el mismo para todos los marcos DI. Sin embargo, por supuesto puede usar la característica del módulo Ninject si lo desea.

6

En cuanto a alcance una aplicación MVC tiene que tener un CompositionRoot diferente de un servicio de Windows. Sugiero que intente organizar tanto como sea posible con los módulos de características (para aquellas partes que son independientes de la aplicación) y todas las demás vinculaciones directamente en CompositionRoot del proyecto MVC o WindowsService.

Otro enfoque muy bueno es definir un conjunto común de convenciones que le ayude a expresar las inquietudes más vinculantes en unas pocas líneas. Por lo tanto sus aplicaciones podrían tener el siguiente para fijaciones:

MVC de la aplicación

Bind(c => c.FromAssemblyContaining<IRepository>() 
      .SelectAllClasses() 
      .InheritedFrom<IRepository>() 
      .Configure(b => b.InRequestScope())); 

Su Servicio de Windows App

Bind(c => c.FromAssemblyContaining<IRepository>() 
      .SelectAllClasses() 
      .InheritedFrom<IRepository>() 
      .Configure(b => b.InThreadScope())); 

En mi punto de vista combinado con una estructuración orientada función del criterio de la Convención es la más limpio.

Cuestiones relacionadas