Brandon
Brandon

Reputation: 69973

How do I add a service layer to my UnitOfWork using Ninject?

I hit a little road block trying to set up DI.

So given that I have this controller:

public UsersController(IUnitOfWork unitOfWork)
{
   // Stuff
}

I previously had this UnitOfWork implementation that accessed my repositories directly.

public class UnitOfWork : IUnitOfWork, IDisposable
{
  private readonly DbContext _context = new DbContext();

  public IRepository<User> Users { get { return new UserRepository(_context); } }
}

Ninject UnitOfWork code: Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();

Now this actually works fine. However I want to change it so that instead of UnitOfWork containing the repositories, it contains services which take in multiple repositories.

public class UserService : IUserService
{
  private readonly IUserRepository _userRepository;
  public UserService(IUserRepository userRepository, /* Other repositories */)
  {
    _userRepository = userRepository;
  }
}

Right now the only solution I can come up with is

public class UnitOfWork : IUnitOfWork, IDisposable
{
  private readonly DbContext _context = new DbContext();

  public IUserService UserService
  {
    get
    {
      return new UserService(new UserRepository(_context), /* etc. */);
    }
  }
}

This doesn't seem right. Now I'm creating all of the service dependencies myself.

I've seen posts on StackOverflow mentioning that it's better to have services access the multiple repositories and handle the business logic rather than having the controller (MVC application) worry about it.

Can't find any specifics on how to actually do this properly though. So how could I use Ninject to do what I'm after?

Upvotes: 0

Views: 596

Answers (1)

Ruben Bartelink
Ruben Bartelink

Reputation: 61795

Sorry, this is really an overgrown comment but hey...

A DbContext is a Unit Of Work, and creating a monster unit of work that straddles two DbContexts is definitely not a general pattern (hence your question sez you)

The comments on this answer here to a related question stray into the same territory.

Questions to ask yourself before you try to solve it this way:

  • What are you attempting to accomplish with this Unit of Work of mine beyond having a DbContext?
  • Does this need to be transactional?
  • Is there actually a missing service in the middle which should have its own data that references the two other ones?
  • Does this need to straddle two Bounded Contexts? What if you need to pull them part again later?
  • If I had 3 cases of this in my app would I keep merging them into one BC? 4?

For me the crux is that creating your own UoW to wrap a UoW:

  1. doesn't buy you much
  2. makes it harder to reason about your core problem

If you are still going to shoehorn this stuff in with Ninject in the way, the technical approach is to use the Context Preservation and Factory Extensions together - go read the examples in their wikis. If you decide to strip things back, they probably still have parts to play too.

UPDATE: I catch your drift now - it's important to link to stuff like that which is guiding your thinking up front. First, go read this. Next, decide if you're going to have a Unit of Work. If so, who and where is going to Commit and Dispose it?

If the Repositories and/or Services are going to share either a UoW or a DBContext and something is going to centrally Commit stuff, then you bind the things to be Shared/Disposed InRequestScope to have it a) get a single shared instance b) get Disposal

My main concern remains whether all these abstractions are all actually benefiting things - are you really writing tests that use the abstractions (and not just for the first few)? Are you really going to switch repositories. I personally would go read the DDD book a few times as a better time investment.

I realise a lot of this is highly patronising and this is just a simplified question representing part of a larger problem, but I've just seen too many questions recently prompted by the intersection of wrapping layers and DI container trickery hence my rant. Rant complete, thanks!

Upvotes: 2

Related Questions