51

I want to create a unit of work class that wraps around repositories in a similar way to this.

The problem I'm having is trying to implement dependency injection by replacing the generic repositories in the example with an IRepository interface. In the uow in the linked article they use getters to check if the repository is instantiated and if it isn't then instantiate it.

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

Which is strongly coupled.

I can see two ways around this.

  1. Use constructor injection.
  2. Use setter injection.

The problem with 1 is that if I inject all the repositories I have to instantiate each repository even if I don't use them in that particular unit of work instance. Thus incurring the overhead of doing so. I was imagining using one, database-wide, unit of work class so this would lead to a lot of needless instantiating and a gigantic constructor.

The problem with 2 is that it would be easy to forget to set and end up with null reference exceptions.

Is there any sort of best practices in this scenario? And are there any other options I have missed?

I'm just getting in to dependency injection and have done all the research I can find on the topic but I may be missing something key.

rashleighp
  • 1,226
  • 1
  • 13
  • 21
  • 1
    What would be the problem with using [`Lazy`](http://msdn.microsoft.com/en-us/library/dd642331.aspx) with constructor injection, so it is only loaded when you actually use it? I know not all IoC containers support `Lazy` out of the box. – Nick Freeman Apr 17 '13 at 16:48
  • 2
    Is there actually any benefit to this? I'd be substituting the creation of one object for the creation of another. I doubt that a repository (whose constructor just assigns a dependency of context) would have to big an overhead when being created. – rashleighp Apr 19 '13 at 19:05
  • not saying it is the way to go, but you said the problem with constructor inject was incurring the overhead of instantiating each repository. The creation of a `Lazy` is practically nothing. – Nick Freeman Apr 19 '13 at 19:50

3 Answers3

70

A way to approach this is to not make the UnitOfWork responsible for creating each Repository through Container injection, but instead to make it the responsibility of each Repository to ensure that the UnitOfWork knows of its existence upon instantiation.

This will ensure that

  • your UnitOfWork doesn't need to change for each new Repository
  • you are not using a service locator (considered by many to be an anti-pattern)

This is best demonstrated with some code - I use SimpleInjector so the examples are based around this:

Starting with the Repository abstraction:

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

and the UnitOfWork

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

Each Repository must register itself with the UnitOfWork and this can be done by changing the abstract parent class GenericRepository to ensure it is done:

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

Each real Repository inherits from the 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) { }
}

Add in the physical implementation of UnitOfWork and you're all set:

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

The container registration can be set up to automatically pick up all the defined instances of IRepository and register them with a lifetime scope to ensure they all survive for the lifetime of your transaction:

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

With these abstractions and an architecture built around DI you have a UnitOfWork that knows of all Repository's that have been instantiated within any service call and you have compile time validation that all of your repositories have been defined. Your code is open for extension but closed for modification.

To test all this - add these classes

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

Add these lines to BootStrapper.Configure()

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

Put a break-point against the line of code:

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

And finally, run this 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();
        }
    }
}

You'll find the code stops at the break point and you have one active instance of a IRepository ready and waiting to Submit() any changes to the database.

You can decorate your UnitOfWork to handle transactions etc. I will defer to the mighty .NetJunkie at this point and recommend you read these two articles here and here.

qujck
  • 14,388
  • 4
  • 45
  • 74
  • I like the idea to a large extent but I was looking to have a single context for the unit of work rather than each repository have their own. If I were to implement that here the unit of work would have to pass the context to the repository and the repository would have to register with the unit of work. Wouldn't this mean they are tightly coupled? Sorry if I am incorrect about any of this, IOC and dependency injection are very new to me. – rashleighp Apr 19 '13 at 14:14
  • 3
    You can register the context with the Container and have that injected into the object constructors of the `UnitOfWork` and the `Repository`'s. Registering the `Context` with the `lifetimeScope` shown above will ensure the same instance is injected to all the objects. – qujck Apr 19 '13 at 14:38
  • @rashleighp I've only been using IoC for the last 6 months. Once you get the hang of it and get it set up - the magic just happens. Here's another link that may help: http://www.codeproject.com/Articles/51007/Simple-Injector – qujck Apr 19 '13 at 16:09
  • 1
    It seems so obvious now you've said it but I didn't know that was something you could do. Yeah that will work well. – rashleighp Apr 19 '13 at 19:44
  • 1
    Great answer, thank you! This helped me through the one last piece that I was having a problem with in my solution. – M.Ob Jun 04 '14 at 13:12
  • 2
    This is an elegant solution for the whole repository/UOW scenario - very comprehensive code examples and well explained. Regarding the .net junkie articles linked at the end of the answer - ive used this approach on solutions in production right now and it worked very very well. – Baldy Jul 30 '14 at 13:48
  • When do the repositories get instantiated using this approach? If no one is taking dependency on them, they will not be created, am I missing something? –  Mar 19 '16 at 23:02
  • @S.Peter Yes, assuming that's the way you've configured your DI to work. – qujck Mar 20 '16 at 08:52
  • Hi qujck, it's been long since you answered this. But can you comment about how to have a scoped lifetime, in case I want the UOW be per controller? – Victor Mukherjee Jun 16 '16 at 07:12
  • Hi @VictorMukherjee, [here](https://simpleinjector.readthedocs.io/en/latest/lifetimes.html)'s the link to SimpleInjector's lifetime management section – qujck Jun 16 '16 at 08:13
  • This is blowing my mind. So should I see all my repos in `_repositories` of a UnitOfWork instance? Would I access the dictionary of _repositories using enums then? many thanks – Ian Jul 06 '16 at 15:36
  • 1
    @Kleky, you can enumerate over the dictionary. It doesn't need to be a dictionary: a `HashSet<>` would be just as good. – qujck Jul 06 '16 at 19:36
  • @qujck, thanks! So they should all appear in there then, as I'm not seeing any at all. `RegisterManyForOpenGeneric` is obsolete now, so I used `container.RegisterCollection(typeof(IRepository), new[] {typeof(IRepository)});` but when I inspect the UOW in debug of a controller, there are no repos loaded and I can't see why. – Ian Jul 07 '16 at 08:15
  • 1
    `RegisterManyForOpenGeneric` is now `Register`, not `RegisterCollection`. Repository instances will only appear in the uow if they are referenced elsewhere in the object graph. – qujck Jul 07 '16 at 10:40
  • Thanks. I think it's now sinking in. – Ian Jul 07 '16 at 11:10
  • I didn't quite get how `IRepository departments)` in SomeActivity would resolve to the `DepartmentRepository` – Ian Jul 07 '16 at 11:16
  • Can you create a small example and ask a new question here on SO or on [GitHub](https://github.com/simpleinjector/SimpleInjector/issues)? – qujck Jul 07 '16 at 11:41
  • @qujck thanks, question is [here](http://stackoverflow.com/questions/38245123/register-generic-types-in-simpleinjector-version-3) – Ian Jul 07 '16 at 12:12
  • 1
    Love this answer, gracias Mister @qujck ! – Jeancarlo Fontalvo Jul 11 '17 at 16:39
  • 1
    This is a great answer, the only thing I don't get is the Methode Submit on IRepository. What is its purpose and where is its implementation? If it is just calling context.SaveChanges() than it wouldn't make sense, as there were multiple calls to SaveChanges(). – iappwebdev Oct 23 '19 at 12:10
  • @iappwebdev `Submit()` would see an individual repository update its related table. `SaveChanges()` calls the `Submit()` methods for each repository it has a reference to, managing the transaction etc as appropriate. – qujck Oct 23 '19 at 13:43
  • 1
    I got it, thank you. In my case I'm working with EF, so I'm not updating single tables but calling SaveChanges on DBContext, so my Commit method would just call DbContext.SaveChanges() – iappwebdev Oct 23 '19 at 17:26
  • @iappwebdev How did you solve this using EF ? Did you, put the `_context.SaveChangesAsync()` in the `Submit()` method or the `Commit()` method ? – Zsombi Apr 01 '21 at 13:40
  • 1
    @Zsombi it goes into the `Commit` method – qujck Apr 01 '21 at 13:47
  • @qujck So it means, I need to inject the DbContext into UnitOfWork and Repostories aswell ? – Zsombi Apr 01 '21 at 13:52
  • 1
    @Zsombi yes you will – qujck Apr 01 '21 at 14:22
  • 1
    @qujck Thank you. Now I understand it clearly. – Zsombi Apr 01 '21 at 14:26
  • I agree with @iappwebdev . If Submit calls SaveChanges or Commit then this is completely wrong solution !!! Because the whole idea of UnitOfWork is to have a single Commit call !!! – Ashi Mar 12 '22 at 18:01
  • @ashi are you using a transactionscope and multiple units or work? if so then I would agree. Otherwise how are you writing to the database multiple times within a single transaction? – qujck Mar 14 '22 at 08:24
  • @qujck Unit Of Work should have single SaveChanges call. please check this link:https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application#:~:text=The%20unit%20of%20work%20class%20serves%20one%20purpose%3A%20to%20make%20sure%20that%20when%20you%20use%20multiple%20repositories%2C%20they%20share%20a%20single%20database%20context.%20That%20way%2C%20when%20a%20unit%20of%20work%20is%20complete%20you%20can%20call%20the%20SaveChanges – Ashi Mar 15 '22 at 07:10
  • @ashi - I know what the unit of work is - this answer says make a single call to the unit of work save changes - the `commit` method - when you are done. The `commit` method in turn calls all of the `submit` methods for any activated repositories. A single call to save all your data - aka _the unit of work_ – qujck Mar 15 '22 at 10:30
  • @qujck , if we have 2 repositories created during the main scope. And during UnitOfWork.Commit the foreach called the first Repository.Submit but then the app crashed and failed to call the second Repository.Submit. Would this situation rollback the first Repository's changes ? Or we will end with up inconsistent database state? – Ashi Mar 15 '22 at 20:58
  • @ashi to ensure they both rollback you would need to explicitly start and end a transaction - just as you would with a vanilla `DbContext` - after all these are simply abstractions over a `DbContext` - they don't actually write to the database themselves - `DbContext` does – qujck Mar 16 '22 at 08:26
7

Instead of injecting repository instances inject single factory object which will be responsible for creating those instances. Your getters will then use that factory.

Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
4

My solution is UnitOfWork still responsible for creating Repository but I made a GetRepository() factory method in UnitOfWork to do that.

public interface IUnitOfWork : IDisposable
{
    T GetRepository<T>() where T : class;
    void Save();
}

public class UnitOfWork : IUnitOfWork
{
    private Model1 db;

    public UnitOfWork() :  this(new Model1()) { }

    public UnitOfWork(TSRModel1 dbContext)
    {
        db = dbContext;
    }

    public T GetRepository<T>() where T : class
    {          
        var result = (T)Activator.CreateInstance(typeof(T), db);
        if (result != null)
        {
            return result;
        }
        return null;
    }

    public void Save()
    {
        db.SaveChanges();
    }

    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                db.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

public class TestRepository : GenericRepository<Test>, ITestRepository
{
    public TestRepository(Model1 db)
       : base(db)
    {
    }
}

public class TestManager: ITestManager
{
    private IUnitOfWork unitOfWork;
    private ITestRepository testRepository;
    public TestManager(IUnitOfWork unitOfWork)
    {
        this.unitOfWork = unitOfWork;
        testRepository = unitOfWork.GetRepository<TestRepository>();
    }

}
sonmt
  • 66
  • 3
  • Thanks @sonmt and [at]qujck here is an implementation. I had some gaps to fill in. Primarily based off [at]sonmt's solution. But this will run (F5) and is end-to-end. Hope it helps someone! https://github.com/DocGreenRob/TestableGenericRepository – Robert Green MBA Apr 23 '22 at 07:27