7

Estoy utilizando MVC3, Entity Framework v4.3 Code First y SimpleInjector. Tengo varias clases simples que se ven así:Cómo reducir el número de dependencias inyectadas en el controlador

public class SomeThing 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

tengo otra entidad que tiene este aspecto:

public class MainClass 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public virtual AThing AThingy { get; set; } 
    public virtual BThing BThingy { get; set; } 
    public virtual CThing CThingy { get; set; } 
    public virtual DThing DThingy { get; set; } 
    public virtual EThing EThingy { get; set; } 
} 

Cada cosita (actualmente) tiene su propio encargado, de este modo:

public class SomeThingManager 
{ 
    private readonly IMyRepository<SomeThing> MyRepository; 

    public SomeThingManager(IMyRepository<SomeThing> myRepository) 
    { 
     MyRepository = myRepository; 
    } 
} 

Mi MainController en consecuencia sigue:

public class MainController 
{ 
    private readonly IMainManager MainManager; 
    private readonly IAThingManager AThingManager; 
    private readonly IBThingManager BThingManager; 
    private readonly ICThingManager CThingManager; 
    private readonly IDThingManager DThingManager; 
    private readonly IEThingManager EThingManager; 

    public MainController(IMainManager mainManager, IAThingManager aThingManager, IBThingManager bThingManager, ICThingManager cThingManager, IDThingManager dThingManager, IEThingManager eThingManager) 
    { 
     MainManager = mainManager; 
     AThingManager = aThingManager; 
     BThingManager = bThingManager; 
     CThingManager = cThingManager; 
     DThingManager = dThingManager; 
     EThingManager = eThingManager; 
    } 

    ...various ActionMethods... 
} 

En realidad, hay dos veces más dependencias inyectadas en este controlador. Huele. El olor es peor cuando también sabe que hay un Otro Controlador con todas o la mayoría de las mismas dependencias. Quiero refactorizarlo.

Ya sé lo suficiente sobre DI para saber que la inyección de propiedades y el localizador de servicios no son buenas ideas.

No puedo dividir mi MainController, porque es una sola pantalla que requiere que todo esto se muestre y editable con solo hacer clic en el botón Guardar. En otras palabras, un único método de acción posterior lo guarda todo (aunque estoy dispuesto a cambiarlo si tiene sentido, siempre que siga siendo un solo botón Guardar). Esta pantalla está construida con Knockoutjs y se guarda con publicaciones de Ajax si eso hace la diferencia.

Me gustó el uso de un contexto ambiental, pero no estoy seguro de que sea el camino correcto. Me complació el uso de inyectar una fachada también. También me pregunto si debería implementar una arquitectura de comando en este punto. (¿No todo lo anterior simplemente mueve el olor a otro lado?)

Por último, y tal vez independiente de los tres enfoques anteriores, ¿debería tener un único, por ejemplo, LookupManager con métodos explícitos como GetAThings() , GetAThing (id), GetBThings(), GetBThing (id), y así sucesivamente? (Pero luego ese LookupManager necesitaría varios repositorios inyectados en él, o un nuevo tipo de repositorio.)

Dejando de lado mis reflexiones, mi pregunta es, para reiterar: ¿cuál es una buena manera de refactorizar este código para reducir el número loco de ¿dependencias inyectadas?

+0

posible duplicado de [Cómo tratar la sobreinyección del constructor en .NET] (http://stackoverflow.com/questions/4603555/how-to-deal-with-constructor-over-injection-in-net) – Steven

Respuesta

3

Usar una command architecture es una buena idea, ya que esto saca toda la lógica comercial del controlador y le permite agregar cross-cutting concerns sin cambios en el código. Sin embargo, esto no solucionará su problema de constructor over-injection. La solución estándar es mover dependencias relacionadas en un aggregate service. Sin embargo, estoy de acuerdo con Mark en que debe echarle un vistazo al unit of work pattern.

+0

Gracias por las referencias. Me gusta este enfoque.Me refactorizaré a un servicio agregado para mis necesidades inmediatas y luego analizaré más a fondo la arquitectura de comandos y la unidad de trabajo. –

+0

@Steven puede proporcionar un ejemplo de cómo esto podría funcionar con SimpleInjector. He seguido su ejemplo en http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=95 y ha sido un enfoque útil. ¿Cómo podría aplicar una fachada o un servicio agregado? –

+0

@DavidClarke: No estoy seguro de qué mostrar. Eche un vistazo al patrón de Unidad de trabajo; eso es típicamente una clase que envolvería los repositorios. Para evitar la sobreinyección del constructor en la unidad de trabajo, puede inyectar una fábrica que sepa cómo resolver repositorios. – Steven

4

¿Ha considerado utilizar un patrón de diseño de unidad de trabajo? Hay una publicación de a great MSDN sobre la unidad de trabajo. Un extracto de ese artículo:

En cierto modo, se puede pensar en la unidad de trabajo como un lugar para volcar toda código de transacción de manipulación. Las responsabilidades de la Unidad de trabajo son:

  • Gestione las transacciones.
  • Solicite las inserciones, eliminaciones y actualizaciones de la base de datos.
  • Evite las actualizaciones duplicadas. Dentro de un único uso de un objeto Unidad de trabajo, diferentes partes del código pueden marcar el mismo objeto Factura
    como modificado, pero la clase Unidad de trabajo solo emitirá un
    único comando ACTUALIZAR a la base de datos.

El valor de la utilización de una Unidad de patrón de trabajo es para liberar el resto de su código de estas preocupaciones por lo que de otra manera puede concentrarse en lógica de negocio.

Hay varias publicaciones en el blog sobre esto, pero la mejor que he encontrado es cómo implementarlo es here. Hay algunos otros a los que se hace referencia desde este sitio here y here.

Por último, y tal vez independiente de los tres enfoques anteriores, es debo vez tener una sola, por ejemplo, LookupManager con métodos explícitos como GetAThings(), GetAThing (ID), GetBThings(), GetBThing (id), y así sucesivamente? (Pero entonces que LookupManager necesitaría varios repositorios inyectados en ella, o un nuevo tipo de repositorio.)

La unidad de trabajo sería capaz de manejar todos ellos, especialmente si usted es capaz de poner en práctica una repositorio genérico para la mayoría de las necesidades de manejo de su base de datos. Su etiqueta menciona que está utilizando Entity Framework 4.3 ¿verdad?

Espero que esto ayude!

+0

[Aquí] (http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=84) es (todavía) otra variación del patrón de unidad de trabajo. – Steven

+0

¿Creé solo una clase de UnitOfWork de sungle que usan todos mis controladores? Ya tengo un GenericRepository . Esa clase haría referencia a todos los repositorios? –

+0

Sí, eso es exactamente lo que esto haría. Esta unidad individual también administrará la concurrencia de elementos cuando se extraigan de todos los repositorios diferentes, y también se asegurará de que solo haya 1 contexto compartido entre ellos. –

0

Creo que su problema principal es demasiadas capas de abstracción. Está utilizando Entity Framework, por lo que ya tiene una capa de abstracción alrededor de sus datos, agregando dos capas más (una por entidad) a través de un Repositorio y una interfaz Manager que le ha llevado a la gran cantidad de interfaces de las que depende su controlador. No agrega mucho valor, y además, YAGNI.

Me gustaría refactorizar, deshacerte de tus capas de repositorio y administrador, y usar un 'contexto ambiental'.

A continuación, observe los tipos de consultas que su controlador le pide a las capas de administrador. Donde estos son muy simples, no veo problemas para consultar su 'contexto ambiental' directamente en su controlador: esto es lo que haría. Donde sean más complicados, refactorícelos en una nueva interfaz, agrupando las cosas lógicamente (no necesariamente una por Entidad) y use su IOC para esto.

Cuestiones relacionadas