96

(relacionado con esta pregunta, EF4: Why does proxy creation have to be enabled when lazy loading is enabled?).Ioc/DI - ¿Por qué tengo que hacer referencia a todas las capas/conjuntos en la aplicación de entrada?

Soy nuevo en DI, así que tengan paciencia conmigo. Entiendo que el contenedor está a cargo de crear instancias de todos mis tipos registrados, pero para ello se requiere una referencia a todos los archivos DLL en mi solución y sus referencias.

Si no estuviera usando un contenedor DI, no tendría que hacer referencia a la biblioteca EntityFramework en mi aplicación MVC3, solo mi capa empresarial, que haría referencia a mi capa DAL/Repo.

Sé que al final del día todas las DLL están incluidas en la carpeta bin pero mi problema es tener que hacer referencia explícitamente a través de "agregar referencia" en VS para poder publicar un WAP con todos los archivos necesarios .

Respuesta

165

Si no estuviera usando un contenedor DI, no tendría que hacer referencia a la biblioteca EntityFramework en mi aplicación MVC3, solo mi capa comercial que haría referencia a mi capa DAL/Repo.

Sí, eso es exactamente la situación DI trabaja muy duro para evitar :)

Con el código fuertemente acoplado, cada biblioteca puede sólo tienen unas pocas referencias, pero éstos a su vez tienen otras referencias, creando un profundo gráfico de dependencias, así:

Deep Graph

Debido a que el gráfico de dependencias es profundo, significa que la mayoría de las bibliotecas arrastran una gran cantidad de otras dependencias - por ejemplo, en el diagrama, Biblioteca C arrastra a lo largo de Biblioteca H, Biblioteca E, Biblioteca J, Biblioteca M, Biblioteca K y Biblioteca N. Esto hace que sea más difícil reutilizar cada biblioteca independientemente del resto, por ejemplo, in unit testing.

Sin embargo, en una aplicación de acoplamiento flexible, moviendo todas las referencias a la Composition Root, el gráfico dependencia se aplana severamente:

Shallow Graph

Como se ilustra por el color verde, ahora es posible reutilizar Biblioteca C sin arrastrar a lo largo de las dependencias no deseadas.

Sin embargo, dicho todo esto, con muchos Contenedores DI, usted no tiene tiene para agregar referencias duras a todas las bibliotecas requeridas. En su lugar, puede usar vinculante tarde ya sea en forma de escaneo de ensamblaje basado en convenciones (preferido) o configuración XML.

Cuando hace eso, sin embargo, debe recordar copiar los ensamblajes a la carpeta bin de la aplicación, porque eso ya no ocurre automáticamente. Personalmente, rara vez encuentro que valga la pena ese esfuerzo extra.

+3

Muchas gracias, esto ahora tiene perfecto sentido ... Necesitaba saber si esto era por diseño. En cuanto a imponer el uso correcto de las dependencias, he implementado un proyecto separado con mi iniciador DI como Steven mencionado a continuación, donde hago referencia al resto de las bibliotecas. Este proyecto es evaluado por la aplicación de punto de entrada y al final de la construcción completa, esto hace que todos los dlls necesarios estén en la carpeta bin. ¡Gracias! – diegohb

+2

@Mark Seemann ¿Es esta pregunta/respuesta específica de Microsoft? Me gustaría saber si esta idea de mover todas las dependencias al "punto de entrada de la aplicación" tiene sentido para un proyecto de Java EE/Spring usando Maven ... ¡gracias! –

+4

Esta respuesta se aplica más allá de .NET. Es posible que desee consultar el capítulo * Principios de diseño de paquetes * de Robert C. Martin, por ejemplo. [Desarrollo de software ágil, principios, patrones y prácticas] (http://amzn.to/195wrPB) –

56

Si no estaba usando un contenedor DI, no tendría que hacer referencia a la biblioteca ADO.NET Entity Framework en mi aplicación MVC3

Incluso cuando se utiliza un contenedor de DI, usted no tiene que dejar su EF de referencia de proyecto MVC3, pero usted (implícitamente) elige hacer esto implementando el Composition Root (la ruta de inicio donde compila sus gráficos de objetos) dentro de su proyecto MVC3. Si eres muy estricto con respecto a la protección de los límites arquitectónicos mediante ensamblajes, puedes mover tus elementos de presentación Composition Root o your (MVC) a una biblioteca de clases.

En la primera opción, permite que su proyecto MVC3 haga referencia a este ensamblaje separado de 'arranque automático', y hará referencia a todos los demás ensambles en su solución, además de hacer referencia a su biblioteca de contenedor DI. El problema con esto es que este proyecto de arranque no puede hacer referencia a los tipos ubicados en el proyecto MVC3 (porque provocará una dependencia de ensamblado cíclico). Estos tipos tienen que ser movidos al proyecto bootstrapper (que posiblemente necesite referenciar System.Web.Mvc), o usted necesita mantener una pequeña parte de la configuración del contenedor dentro de su aplicación MVC3. También tenga en cuenta que su proyecto MVC todavía hace referencia indirectamente a todos los demás ensamblados a través del nuevo ensamblaje de arranque, porque las dependencias de ensamblaje son transitivas. Aunque colocar la raíz de composición en un ensamblaje separado es algo válido, la mayoría de DI Purist (incluyéndome a mí) generalmente solo mueve la raíz de composición a una biblioteca de clase cuando hay múltiples aplicaciones finales (es decir, una aplicación web + web servicio + servicio de Windows) que usan la misma capa de negocios. Cuando tengo una sola aplicación, mantengo la raíz de composición dentro de mi aplicación final.

La segunda opción es mover todas las clases relacionadas con MVC (vistas, controladores, etc.) desde el proyecto de inicio a una biblioteca de clases. Esto permite que este nuevo conjunto de capa de presentación permanezca desconectado del resto de la aplicación. Su proyecto de aplicación web se convertirá en un shell muy delgado con la lógica de inicio requerida. El proyecto de aplicación web será la raíz de composición que hace referencia a todos los demás ensamblajes.

Extraer la lógica de presentación a una biblioteca de clases puede complicar las cosas al trabajar con MVC. Será más difícil conectar todo, ya que los controladores y vistas, imágenes, archivos css, etc. no están en el proyecto de inicio. Esto es probablemente factible, pero llevará más tiempo configurarlo.

Ambas opciones tienen sus inconvenientes y es por eso que generalmente les aconsejo que mantengan la raíz de composición en el proyecto web. Muchos desarrolladores no quieren que su ensamblaje MVC dependa del ensamblaje DAL, pero eso no es realmente un problema. No olvide que los ensamblajes son un artefacto de despliegue; divide el código en varios conjuntos para permitir que el código se implemente por separado. Por otro lado, una capa arquitectónica es un artefacto lógico. Es muy posible (y común) tener múltiples capas en el mismo conjunto. En este caso, tendremos la raíz de composición (capa) y la capa de presentación en el mismo proyecto de aplicación web (por lo tanto, en el mismo ensamblaje). Y aunque ese ensamblado hace referencia al ensamblaje que contiene el DAL, la Presentación Capa aún no hace referencia al Acceso a datos Capa. Esta es una gran distinción. Por supuesto, cuando hacemos esto, perdemos la capacidad del compilador para verificar esta regla de arquitectura en tiempo de compilación, pero esto no debería ser un problema. La mayoría de las reglas arquitectónicas en realidad no pueden ser verificadas por el compilador y siempre hay algo como el sentido común. Y si no hay sentido común en su equipo, siempre puede usar revisiones de código (que cada equipo debe hacer siempre por IMO). También puede usar una herramienta como NDepend (que es comercial), que le ayuda a verificar sus reglas arquitectónicas.Cuando integra NDepend con su proceso de compilación, puede advertirle cuando alguien revisó el código que infringe dicha regla de arquitectura.

+0

Un proyecto separado para bootstrapping fue mi solución ya que no tenemos ndepend y nunca lo he usado antes. Aunque lo analizaré, ya que parece una mejor manera de lograr lo que estoy tratando de hacer cuando solo habrá una aplicación final. – diegohb

+1

El último párrafo es excelente y está empezando a ayudarme a cambiar de opinión sobre lo estricto que soy para mantener las capas en conjuntos separados. Tener dos o más capas lógicas en un ensamblaje está realmente bien si emplea otros procesos en la escritura de código (como revisiones de código) para garantizar que no haya referencias de clases DAL en su código de interfaz de usuario y viceversa. – BenM

3

Si no estaba usando un contenedor DI, no tendría que hacer referencia a la biblioteca ADO.NET Entity Framework en mi aplicación MVC3, sólo mi capa de negocio que haría referencia a mi capa DAL/Repo.

Puede crear un proyecto separado llamado "DependencyResolver". En este proyecto, debe hacer referencia a todas sus bibliotecas.

Ahora la capa de interfaz de usuario no necesita NHibernate/EF o cualquier otra biblioteca que no sea relevante para la interfaz de usuario, excepto la de Castle Windsor a la que se hace referencia.

Si quiere ocultar Castle Windsor y DependencyResolver desde su capa de interfaz de usuario, puede escribir un HttpModule que llame al registro de IoC.

I tienen solamente un ejemplo para StructureMap:

public class DependencyRegistrarModule : IHttpModule 
{ 
    private static bool _dependenciesRegistered; 
    private static readonly object Lock = new object(); 

    public void Init(HttpApplication context) 
    { 
     context.BeginRequest += (sender, args) => EnsureDependenciesRegistered(); 
    } 

    public void Dispose() { } 

    private static void EnsureDependenciesRegistered() 
    { 
     if (!_dependenciesRegistered) 
     { 
      lock (Lock) 
      { 
       if (!_dependenciesRegistered) 
       { 
        ObjectFactory.ResetDefaults(); 

        // Register all you dependencies here 
        ObjectFactory.Initialize(x => x.AddRegistry(new DependencyRegistry())); 

        new InitiailizeDefaultFactories().Configure(); 
        _dependenciesRegistered = true; 
       } 
      } 
     } 
    } 
} 

public class InitiailizeDefaultFactories 
{ 
    public void Configure() 
    { 
     StructureMapControllerFactory.GetController = type => ObjectFactory.GetInstance(type); 
      ... 
    } 
} 

El DefaultControllerFactory no utiliza el contenedor IoC directamente, pero los delegados a métodos de contenedores COI.

public class StructureMapControllerFactory : DefaultControllerFactory 
{ 
    public static Func<Type, object> GetController = type => 
    { 
     throw new InvalidOperationException("The dependency callback for the StructureMapControllerFactory is not configured!"); 
    }; 

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) 
    { 
     if (controllerType == null) 
     { 
      return base.GetControllerInstance(requestContext, controllerType); 
     } 
     return GetController(controllerType) as Controller; 
    } 
} 

El delegado GetController se encuentra en un registro StructureMap (en Windsor debe ser un instalador).

+1

me gusta esto incluso mejor de lo que terminé haciendo, los módulos son geniales. Entonces, ¿dónde haría la llamada a Container.Dispose()? Evento ApplicationEnd o EndRequest dentro del módulo ...? – diegohb

+0

¡Qué forma tan extraña de hacer las cosas! ¿Por qué quiere hacer esto en lugar de simplemente invocar EnsureDependenciesRegistered desde Application_Start of the Global.asax? – Steven

+1

@Steven Porque Global.asax está en su capa de interfaz de usuario MVC. El HttpModule estaría en el Proyecto DependencyResolver. – Rookian

0

+ Existe una dependencia: si un objeto instancia otro objeto. + No hay dependencia: si un objeto espera una abstracción (inyección de contructor, inyección de método ...)

+ Referencias de ensamblaje (haciendo referencia a dll, webservices ..) son independientes del concepto de dependencia, porque para resolver una abstracción y poder compilar el código, la capa debe hacer referencia a él.

Cuestiones relacionadas