Pregunta

Recién estoy comenzando con DI y pruebas unitarias y me encontré con un problema que estoy seguro es una obviedad para los desarrolladores más experimentados:

Tengo una clase llamada MessageManager que recibe datos y los guarda en una base de datos.Dentro del mismo ensamblado (proyecto en Visual Studio) he creado una interfaz de repositorio con todos los métodos necesarios para acceder a la base de datos.La implementación concreta de esta interfaz se encuentra en un ensamblaje separado llamado DataAccess.

Por lo tanto, DataAccess necesita una referencia del proyecto a MessageManager para conocer la interfaz del repositorio.Y MessageManager necesita una referencia de proyecto a DataAccess para que el cliente de MessageManager pueda inyectar una implementación concreta de la interfaz del repositorio.Por supuesto, esto no está permitido.

Podría mover la interfaz al ensamblaje de acceso a datos, pero creo que la interfaz del repositorio debe residir en el mismo ensamblaje que el cliente que la usa.

Entonces ¿qué he hecho mal?

¿Fue útil?

Solución

¿Está utilizando un contenedor de inversión de control?Si es así, la respuesta es sencilla.

El conjunto A contiene:

  • Administrador de mensajes
  • IRepositorio
  • ContenedorA (agregar MessageManager)

El conjunto B contiene (y el conjunto A de referencia):

  • El repositorio implementa IRepository
  • ContainerB extiende ContainerA (agregar repositorio)

El ensamblaje C (o B) iniciaría la aplicación/preguntaría al contenedor MessageManager, que sabría cómo resolver MessageManager. y el repositorio I.

Otros consejos

Debe separar su interfaz de cualquiera de los ensamblajes.Poner la interfaz junto con el consumidor o el implementador anula el propósito de tener la interfaz.

El propósito de la interfaz es permitirle inyectar cualquier objeto que implemente esa interfaz, sea o no el mismo ensamblado al que pertenece su objeto DataAccess.Por otro lado, debes permitir que MessageManager consuma esa interfaz sin la necesidad de consumir ninguna implementación concreta.

Coloque su interfaz en otro proyecto y el problema estará resuelto.

Sólo tienes dos opciones:agregue un ensamblaje para contener la interfaz o mueva la interfaz al ensamblaje de DataAccess.Incluso si está desarrollando una arquitectura donde la clase DataAccess algún día puede ser reemplazada por otro implementador (incluso en otro ensamblado) de la interfaz del repositorio, no hay razón para excluirla del ensamblado DataAccess.

Creo que deberías mover la interfaz del repositorio al ensamblado de DataAccess.Entonces DataAccess ya no necesita hacer referencia a MessageManager.

Sin embargo, sigue siendo difícil decirlo ya que no sé casi nada sobre su arquitectura...

Con frecuencia, puede resolver problemas de referencias circulares utilizando la inyección de definidor en lugar de la inyección de constructor.

En pseudocódigo:

Foo f = new Foo();
Bar b = new Bar();
f.setBar(b);
b.setFoo(f);

inversión de dependencia está en juego:

Los módulos de alto nivel no deberían depender de módulos de bajo nivel.Ambos deberían depender de abstracciones.Las abstracciones no deberían depender de los detalles.Los detalles deberían depender de abstracciones.

La abstracción de la que dependen las clases en el ensamblado de DatAccess debe estar en un ensamblado separado de las clases de DataAccess y la implementación concreta de esa abstracción (MessageManager).

Sí, eso son más asambleas.Personalmente eso no es gran cosa para mí.No veo un gran inconveniente en los ensamblajes adicionales.

Podrías dejar la estructura como la tienes actualmente (sin la dependencia de MessageManager a DataAccess que causa el problema) y luego tener MessageManager cargar dinámicamente la implementación concreta requerida en tiempo de ejecución utilizando el System.Reflection.Assembly clase.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top