Search code examples
asp.net-mvc-4dependency-injectionentity-framework-5ninject

Injecting DbContext InRequestScope does not return the same object instance


Problem:

I use Ninject in my webapplication as IoC-Framework. I have tried to inject the Entity-Framework DbContext class to several classes in my code. I want to share a object instance by request. So I tried using the InRequestScope method from ninject. Problem is the instance is not shared. Instead I always get a new instance of DbContext.

Code:

First of all my implementation of DbContext class:

public class BudgetContext : DbContext, IUnitOfWork
{
    public BudgetContext() : base("DefaultConnection") 
    {
        Database.SetInitializer<BudgetContext>(new DropCreateDatabaseIfModelChanges<BudgetContext>());
    }

    public DbSet<Expense> Expenses { get; set; }

    public int Commit()
    {
        return SaveChanges();
    }
}

I want to inject the BudgetContext (my DbContext and also UnitOfWork) in two different places:

public class UnitOfWorkAttribute : ActionFilterAttribute
{
    [Inject]
    public IUnitOfWork UnitOfWork { get; set; }  // injecting DbContext here (1)

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);
        if (filterContext.Exception == null)
        {
            UnitOfWork.Commit();
        }
    }
}

public class ExpenseRepository : IExpenseRepository
{
    private readonly BudgetContext context;

    public ExpenseRepository(IUnitOfWork context)  // injecting DbContext here (2)
    {
        this.context = (BudgetContext)context;
    }

    public void Add(Expense expense)
    {
        context.Expenses.Add(expense);
    }
}

Both injected DbContext classes should be the same object instance for one request. My Ninject configuration in NinjectWebCommon.cs looks like this:

...
private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<IUnitOfWork>().To<BudgetContext>().InRequestScope();
    kernel.Bind<IExpenseRepository>().To<ExpenseRepository>();
    // ...
    kernel.Bind<ExpensesController>().To<ExpensesController>();
}    
... 

Did I configure the Ninject-Framework correct? What am I missing? Thanks for helping me out.

**Update:**

I have implemented a factory to create my UnitOfWork(which basically is my DbContext class). The implementation of the factory looks like this:

public class UnitOfWorkFactory : IUnitOfWorkFactory
{
    private IUnitOfWork unitOfWork;

    public IUnitOfWork Get()
    {
        if (unitOfWork == null)
            unitOfWork = new BudgetContext();
        return unitOfWork;
    }
}

In Ninject I use the following configuration for the new factory:

kernel.Bind<IUnitOfWorkFactory>().To<UnitOfWorkFactory>().InRequestScope();

In my attribute I inject it this way:

[Inject]
public IUnitOfWorkFactory UnitOfWorkFactory{ get; set; } 
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
    base.OnActionExecuted(filterContext);
    if (filterContext.Exception == null)
    {
        var unitOfWork = UnitOfWorkFactory.Get();
        unitOfWork.Commit();
    }
}

Unfortunately I have no success. My DbContext always gets resetted after it reaches the attribute class.


Solution

  • Filters are cached by the MVC framework. Do not use InRequest scoped references but inject a factory instead.

    https://github.com/ninject/ninject.web.mvc/wiki/Filters-and-Scoped