Dependency Injection: Ninject – Registering Two Services to the Same Instance of an Object


In on of my recent ASP.NET MVC projects I was implementing the UnitOfWork pattern while utilizing the Entity Framework Code First approach for data access. I have seen many examples of the UnitOfWork pattern where the repositories are actually contained within the UnitOfWork class but since the Entity Framework already supports the UnitOfWork pattern out of the box, I decided to go with a different approach.

To jump to the solution to the problem, click here.

Using the Entity Framework Code First approach, a class inheriting from the DbContext class is created and is used to handle all the database interaction. Whenever you perform any work using the DbContext, the changes are not actually persisted to the database until you call the SaveChanges method. As I said, the UnitOfWork pattern already implemented. To enforce the UnitOfWork pattern, I created an IUnitOfWork interface and ensured that my DbContext class implemented that interface.

public interface IUnitOfWork
{
    void Commit();
}

public class DatabaseContext : DbContext, IUnitOfWork
{
    public DbSet<Company> Companies { get; set; }
    public DbSet<Profile> Profiles { get; set; }
    ...
 
    public DatabaseContext ()
    {
        
    }

    public DatabaseContext (string connectionString)
    {
        this.Database.Connection.ConnectionString = connectionString;
    }

    public void Commit()
    {
        base.SaveChanges();
    }
}

Each of the repositories in the application accepts a class that inherits from DbContext and uses that for all database interaction.

public class EntityFrameworkRepository<T> : IRepository<T> where T : class, IEntity
{
    private DbContext context;
    private DbSet<T> dbSet;

    public EntityFrameworkRepository(DbContext context)
    {
        if (context == null) { throw new ArgumentNullException("context"); }
            
        this.context = context;
        this.dbSet = this.context.Set<T>();
    }
}

I then have a service layer that accepts any number of repositories and lastly, each Controller accepts any number of services and a IUnitOfWork implementation.

public class AccountController : BaseController
{
    private IUnitOfWork unitOfWork;
    private IAccountService accountService;
    private IProfileService profileService;

    public AccountController(IUnitOfWork unitOfWork, IAccountService accountService, IProfileService profileService)
    {
        this.unitOfWork = unitOfWork;
        this.accountService = accountService;
        this.profileService = profileService;
    }
}

This allows the Controller to interact with any number of repositories and then after all operations are complete, to call the IUnitOfWork.Commit() method. Here is the issue. With this implementation, the IUnitOfWork instance supplied to the AccountController and the DbContext provided to the repositories all need to be the same instance of the DatabaseContext class for the UnitOfWork pattern to work. Here we my first attempt at doing this.

private static void RegisterServices(IKernel kernel)
{           
    kernel.Bind<IUnitOfWork>().To<DatabaseContext>();
    kernel.Bind<DbContext>().To<DatabaseContext>();
}   

This definitely didn’t work as it created new instances of the DatabaseContext class for each repository and each IUnitOfWork. Using Ninject, to ensure that the same DbContext instance is used for all dependencies in a single http request, you simply need to add InRequestScope() to the end of the registration. So I tried the following.

private static void RegisterServices(IKernel kernel)
{           
    kernel.Bind<IUnitOfWork>().To<DatabaseContext>().InRequestScope();
    kernel.Bind<DbContext>().To<DatabaseContext>().InRequestScope();
}   

This was closer as all the repositories were using the same DbContext but when the Controller requested an IUnitOfWork, Ninject created a new instance of the DatabaseContext class and thus the IUnitOfWork pattern did not work. I needed to somehow ensure that the same instance of the DatabaseContext class was used for all DbContext and IUnitOfWork requests. After some research, I figured out that the following code will resolve the problem.

private static void RegisterServices(IKernel kernel)
{       
    // This gives us the ability to perform multiple operations
    // on multiple repositories in a single transaction.
    kernel.Bind<DatabaseContext>().ToSelf().InRequestScope();
    kernel.Bind<IUnitOfWork>().ToMethod(ctx => ctx.Kernel.Get<DatabaseContext>());
    kernel.Bind<DbContext>().ToMethod(ctx => ctx.Kernel.Get<DatabaseContext>());
}   

In the code above, we are first binding the DatabaseContext class to itself indicating that only one instance should be created per http request. This ensures that whenever a DatabaseContext instance is required, the same one is used. Then, to bind that same DatabaseContext instance to both IUnitOfWork and DbContext request, we need to bind those requests to a method which requests a DatabaseContext instance from the Ninject kernel. And that’s it!

Follow

Get every new post delivered to your Inbox.

Join 68 other followers