Dependency injection in unit of work pattern using repositories

c# dependency-injection entity-framework unit-of-work

Question

Similar to this, I want to develop a work class course that revolves around repositories.

I'm experiencing trouble trying to construct dependency injection by using an IRepository interface in instead of the example's generic repositories. In the uow in the related article, they use getters to determine whether the repository has already been instantiated and, if not, to do so.

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

It has a close coupling.

I see two workarounds for this.

  1. inject code into constructors.
  2. Setter injection is used.

The issue with solution 1 is that, even if I don't use each repository in that specific unit of work instance, I still have to instantiate them all. resulting in the associated overhead. I had in mind creating a single, database-wide unit of labor class, which would result in a massive constructor and a lot of pointless instantiating.

The issue with 2 is that null reference exceptions could result from forgetting to set it.

Exist any kind of best practices for this situation? Do you think I overlooked any additional options?

I've done all the available literature on dependency injection because I'm just starting started with it, but I could still be overlooking something important.

1
41
4/17/2013 4:03:16 PM

Accepted Answer

One strategy is to avoid making theUnitOfWork responsible for each creationRepository by Container injection, but to make it each individual's obligationRepository to guarantee that theUnitOfWork Upon instantiation, it is aware of its existence.

Thus, it will be certain

  • your UnitOfWork does not require modification for each newRepository
  • You don't use a service finder (considered by many to be an anti-pattern)

The simplest way to show this is with some code, therefore the examples are built around SimpleInjector:

beginning withRepository abstraction:

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

both theUnitOfWork

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

Each Repository must submit a registration form to theUnitOfWork and altering the abstract parent class will accomplish this.GenericRepository to ensure completion:

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

each actualRepository gains from theGenericRepository :

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) { }
}

Include the actual application ofUnitOfWork Now you're ready to go:

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());
    }
}

It is possible to configure the container registration to automatically detect each of the specified instances ofIRepository make ensure they all last for the duration of your transaction, register them with a lifetime scope:

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);
    }
}

You have a DI-centered architecture with these abstractions.UnitOfWork who is aware of allRepository 's that were created during any service request, and you have compile-time verification that each of your repositories has been declared. You have the code open to extending but closed to changing.

Add these classes to test everything.

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();
    }
}

Include these lines inBootStrapper.Configure()

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

Set a breakpoint for the corresponding line of code:

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

Finally, execute the following Console test code:

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();
        }
    }
}

The code terminates at the break point, and there is only one active instance of aIRepository able and willing toSubmit() any database alterations.

Your UnitOfWork can be customized to handle transactions and other tasks. I'll yield to the powerful. At this point, NetJunkie suggests that you read articles here and here.

59
4/18/2013 6:19:44 PM

Popular Answer

ZZZ_tmp


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow