Domanda

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.

È stato utile?

Soluzione

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

Altri suggerimenti

Thanks to @Remo Gloor I figured out that something must be wrong with my attribute. So I changed my implementation and implemented the interface IActionFilter instead of inheriting the class ActionFilterAttribute. Now I can injenct my DbContext InRequest scoped.

The Attribute:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class UnitOfWorkAttribute : Attribute { }
public class UnitOfWorkFilter : IActionFilter
{
    private readonly IUnitOfWork unitOfWork;

    public UnitOfWorkFilter(IUnitOfWork unitOfWork)
    {
        this.unitOfWork = unitOfWork;
    }

    public void OnActionExecuting(ActionExecutingContext filterContext) { }

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

}

The Binding:

kernel.Bind<IUnitOfWork>().To<BudgetContext>().InRequestScope();
kernel.BindFilter<UnitOfWorkFilter>(FilterScope.Action, 0).WhenActionMethodHas<UnitOfWorkAttribute>();

You need three packages

Ninject
Ninject.Web
Ninject.Web.Common

And then you should register NinjectHttpModule in both libraries Ninject.Web.Common and Ninject.Web

public static void Start()
{
    DynamicModuleUtility.RegisterModule(typeof (OnePerRequestHttpModule));
    DynamicModuleUtility.RegisterModule(typeof (Ninject.Web.Common.NinjectHttpModule));
    DynamicModuleUtility.RegisterModule(typeof (Ninject.Web.NinjectHttpModule));
    bootstrapper.Initialize(createKernel);
}

And I'm not sure if you need to add Bind<UnitOfWorkAttribute>().ToSelf()

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top