0

I'm trying to implement unit of work pattern for my repositories in C#/.NET. My plan is to give UoW as a parameter to repositories. Here's an example how it could be used:

using (var uow = new UnitOfWork()) 
{
    var itemRepository = new ItemRepository(uow);
    itemRepository.Add(new Item());
    uow.Commit();
}

Also, for simple operations (when transactions are not needed) repositories should be able to be used without Unit of Work:

var itemRepository = new ItemRepository();
var item = itemRepository.Get(itemId);

UnitOfWork/Repository could get a database connection from ConnectionFactory. Connection factory receives connection options via dependency injection. However, here's my problem: How do repositories get reference to ConnectionFactory instance? Repositories are created manually, so they can't have dependencies injected via constructor. One option would be to have repository factories, which could have their dependencies injected. In that case usage could be like this:

using (var uow = new UnitOfWork()) 
{
    var itemRepository = itemRepositoryFactory.Create(uow);
    itemRepository.Add(new Item());
    uow.Commit();
}

The drawback in that solution is that each repository will need its own factory, and there will be quite many. Are there any other solutions to circumvent this problem?

wekso
  • 39
  • 7
  • Why don't you create *everything* via dependency injection by your `IoC` container (in case of web app) or your `main` method (in case of console type app)? It is not recommended to have any service instantiated *after* the startup phase of your app or the beginning of a `Http request`, for instance. – Aage Sep 15 '21 at 10:47
  • The only component which is not instantiated via DI is the repository. For repository, I must be able to provide UoW instance. One option would be to require UoW parameter for repository methods, but that would be a bit cumbersome to use. – wekso Sep 15 '21 at 10:52
  • just read this at first https://gunnarpeipman.com/ef-core-repository-unit-of-work/ – Serge Sep 15 '21 at 13:41
  • I'm not using Entity Framework, so repositories are a reasonable abstraction around raw sql queries. – wekso Sep 16 '21 at 05:23
  • @wekso Are you using `.Net Core`? Or `.Net Framework` with a Dependency Injection library (like `Ninject`)? What happens if you register the `UnitOfWork` (in *request scope* so it gets disposed *after* the request is done) and request a `repository` in some constructor? – Aage Sep 16 '21 at 12:27
  • @wekso This is another answer of mine concerning `UoW`, does this help? https://stackoverflow.com/a/23316399/2877982 – Aage Sep 16 '21 at 12:31

1 Answers1

0

I would definitely register the UOW as a scoped dependency.

Scoped dependencies live for the lifetime of the container that creates them. Typically, frameworks would generate a child container from the parent container in order to execute some piece of work. For example, ASP.NET Core spawns a child container for a request and then disposes it when the request is finished. This would mean that the UOW instance that is getting injected is the same instance throughout the object graph for that request only.

You can also create your own scopes, if needed. I have done this twice for example:

  • A job scheduler, so that each job ran in it's own scope
  • A message handler, so that each message was processed in its own scope

This is how you would achieve this using Microsoft's DI framework:

var collection = new ServiceCollection();
collection.AddScoped<IUnitOfWork, UnitOfWork>();

var provider = collection.BuildServiceProvider();

var puow = provider.GetRequiredService<IUnitOfWork>();

for(int i = 0; i < 2; i++)
{
    //Create the new scope
    using var childContainer = provider.CreateScope();

    //IUnitOfWork will be a singleton instance in this scope. 
    var c1uow = childContainer.ServiceProvider.GetRequiredService<IUnitOfWork>();
    var c2uow =  childContainer.ServiceProvider.GetRequiredService<IUnitOfWork>();
    // This should true, since they come from the same scope.
    var sameScope = c1uow == c2uow;
    
    //With the requested IUnitOfWork from provider instead, it would be a different instance
    //Therefore, this should be false    
    var diffScope = puow == c1uow;
}

This would allow you to simply inject IUnitOfWork into each repo, without having to create a factory for each repo.

This would work out of the box if you are writing an application in ASP.NET Core. Simply register the dependency as scope, as so

collection.AddScoped<IUnitOfWork, UnitOfWork>();

and then have it injected into the repos that you need. You don't have to worry about creating a new scope since the framework does that for you for each http request that the application receives.

I would really recommend reading Mark Seemann's book "Dependency Injection Principles, Practices, and Patterns". It really goes in depth about what dependency injection is and how it works. Not only that, I find him to be a great writer.

Eric Ruder
  • 463
  • 5
  • 15