patron para inyeccion dummies dependencias c# entity-framework dependency-injection unit-of-work

c# - para - inyeccion de dependencias swift



Inyección de dependencia en el patrón de unidad de trabajo utilizando repositorios (2)

En lugar de inyectar instancias de repositorio, inyecte un solo objeto de fábrica que será responsable de crear esas instancias. Tus compradores usarán esa fábrica.

Quiero crear una unidad de clase de trabajo que envuelva los repositorios de forma similar a this .

El problema que tengo es tratar de implementar la inyección de dependencia reemplazando los repositorios genéricos en el ejemplo con una interfaz IRepository. A continuación, en el artículo vinculado utilizan getters para comprobar si el repositorio está instanciado y, en caso de no ser así, crear una instancia.

public GenericRepository<Department> DepartmentRepository { get { if (this.departmentRepository == null) { this.departmentRepository = new GenericRepository<Department>(context); } return departmentRepository; } }

Que está fuertemente acoplado.

Puedo ver dos formas de evitar esto.

  1. Usar inyección de constructor
  2. Use la inyección setter.

El problema con 1 es que si inyecto todos los repositorios, tengo que crear instancias de cada repositorio incluso si no los utilizo en esa instancia particular de la unidad de trabajo. Por lo tanto, incurrir en los gastos generales de hacerlo. Imaginaba utilizar una clase de unidad de trabajo de toda la base de datos, así que esto daría lugar a una gran cantidad de instancias innecesarias y un constructor gigantesco.

El problema con 2 es que sería fácil olvidarse de establecer y terminar con excepciones de referencia nula.

¿Hay algún tipo de mejores prácticas en este escenario? ¿Y hay otras opciones que me he perdido?

Estoy entrando a la inyección de dependencia y he hecho toda la investigación que puedo encontrar sobre el tema, pero me puede estar perdiendo algo clave.


Una forma de abordar esto es no responsabilizar a UnitOfWork de crear cada Repository través de la inyección de Contenedores, sino de responsabilizar a cada Repository para asegurarse de que UnitOfWork sepa de su existencia en la UnitOfWork de instancias.

Esto asegurará que

  • su UnitOfWork no necesita cambiar para cada nuevo Repository
  • no está usando un localizador de servicios (considerado por muchos como un anti-pattern )

Esto se demuestra mejor con algún código: uso SimpleInjector para que los ejemplos se basen en esto:

Comenzando con la abstracción del Repository :

public interface IRepository { void Submit(); } public interface IRepository<T> :IRepository where T : class { } public abstract class GenericRepository<T> : IRepository<T> where T : class { }

y la UnitOfWork

public interface IUnitOfWork { void Register(IRepository repository); void Commit(); }

Cada Repository debe registrarse en UnitOfWork y esto se puede hacer cambiando la clase padre abstracta GenericRepository para asegurarse de que esté lista:

public abstract class GenericRepository<T> : IRepository<T> where T : class { public GenericRepository(IUnitOfWork unitOfWork) { unitOfWork.Register(this); } }

Cada Repository real hereda de GenericRepository :

public class Department { } public class Student { } public class DepartmentRepository : GenericRepository<Department> { public DepartmentRepository(IUnitOfWork unitOfWork): base(unitOfWork) { } } public class StudentRepository : GenericRepository<Student> { public StudentRepository(IUnitOfWork unitOfWork) : base(unitOfWork) { } }

Agregue la implementación física de UnitOfWork y ya está todo listo:

public class UnitOfWork : IUnitOfWork { private readonly Dictionary<string, IRepository> _repositories; public UnitOfWork() { _repositories = new Dictionary<string, IRepository>(); } public void Register(IRepository repository) { _repositories.Add(repository.GetType().Name, repository); } public void Commit() { _repositories.ToList().ForEach(x => x.Value.Submit()); } }

El registro del contenedor se puede configurar para que recoja automáticamente todas las instancias definidas de IRepository y las registre con un alcance de por vida para garantizar que todas sobrevivan durante la vigencia de su transacción:

public static class BootStrapper { public static void Configure(Container container) { var lifetimeScope = new LifetimeScopeLifestyle(); container.Register<IUnitOfWork, UnitOfWork>(lifetimeScope); container.RegisterManyForOpenGeneric( typeof(IRepository<>), lifetimeScope, typeof(IRepository<>).Assembly); } }

Con estas abstracciones y una arquitectura construida en torno a DI, tiene un UnitOfWork que conoce todos los Repository que se han instanciado en cualquier llamada de servicio y tiene la validación en tiempo de compilación de que se han definido todos sus repositorios. Su código está abierto para la extensión, pero está cerrado para modificaciones .

Para probar todo esto, agrega estas clases

public class SomeActivity { public SomeActivity(IRepository<Department> departments) { } } public class MainActivity { private readonly IUnitOfWork _unitOfWork; public MainActivity(IUnitOfWork unitOfWork, SomeActivity activity) { _unitOfWork = unitOfWork; } public void test() { _unitOfWork.Commit(); } }

Agregue estas líneas a BootStrapper.Configure()

//register the test classes container.Register<SomeActivity>(); container.Register<MainActivity>();

Poner un punto de ruptura contra la línea de código:

_repositories.ToList().ForEach(x => x.Value.Submit());

Y finalmente, ejecute este código de prueba de consola:

class Program { static void Main(string[] args) { Container container = new Container(); BootStrapper.Configure(container); container.Verify(); using (container.BeginLifetimeScope()) { MainActivity entryPoint = container.GetInstance<MainActivity>(); entryPoint.test(); } } }

Verá que el código se detiene en el punto de IRepository y tiene una instancia activa de un IRepository lista y esperando que Submit() IRepository cambios en la base de datos.

Puedes decorar tu UnitOfWork para manejar transacciones, etc. En este punto, me inclinaré por el poderoso .NetJunkie y te recomendaré que leas estos dos artículos here y here .