Ninject WebAPI The operation cannot be completed because the DbContext has been disposed
Asked Answered
J

1

10

So I am using a simple repository pattern with Attributes and Filters as recommended here since I am using the Ninject.Web.WebApi-RC package from NuGet.

This works for the first request but since I have my DbContext in request scope, it is disposed on all subsequent requests.

Here is my attribute:

public class CommunicationKeyValidationAttribute : FilterAttribute
{
}

Here is my filter:

public class CommunicationKeyValidationFilter : AbstractActionFilter
{
    public CommunicationKeyValidationFilter(IRepository repository)
    {
        this.repository = repository;
    }
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        // do stuff
    }
 }

Here is my repository:

public class Repository : IRepository
{
    public Repository(MyDbContext dbContext)
    {
        this.dbContext = dbContext;
    }
}

Here are my Ninject bindings:

this.Kernel.Bind<MyDbContext>().ToSelf().InRequestScope();
this.Kernel.Bind<IRepository>().To<Repository>().InRequestScope();
this.Kernel.BindHttpFilter<CommunicationKeyValidationFilter>(FilterScope.Action)
        .WhenActionMethodHas<CommunicationKeyValidationAttribute>()
        .InRequestScope();

My controller looks like this:

public class HomeController 
{
    [CommunicationKeyValidation]
    public ActionResult Index()
    {
        // do stuff
    }

The issue here is that the constructor on CommunicationKeyValidationFilter is only being called on the first request. Is there a way that I can cause ninject to construct this filter each time it attempts to resolve the filter?

Jennajenne answered 11/3, 2013 at 21:30 Comment(6)
I was able to repro your problem: so I would say this a bug in Ninject which is caused by a feature (bug?) in web.api. It seems Web.Api itself caches the filters: I found this in the autofac web.api integration: "Unlike the filter provider in MVC the one in Web API does not allow you to specify that the filter instances should not be cached. This means that all filter attributes in Web API are effectively singleton instances that exist for the entire lifetime of the application."Atomizer
Yeah, I believe I read the same article. I was wondering if anyone had tackled this by writing a custom filter provider or something where it creates a new instance during each request.Jennajenne
Interestingly Ninject provides two filter providers DefaultFilterProvider and NinjectFilterProvider for Web.API...Atomizer
But it seems it is not the fiterproviders fault. The cashing is done by Web.API in ApiControllerActionSelector which caches the ReflectedHttpActionDescriptors which caches the Filters in the _filterPipeline...Atomizer
As a dirty workaround: don't use constructor injection in your CommunicationKeyValidationFilter but resolve your services in the OnActionExecuting method: public override void OnActionExecuting(HttpActionContext actionContext) { ((IRepository) actionContext.Request.GetDependencyScope().GetService(typeof(IRepository))).Do(); }Atomizer
Yeah, I already tried overriding the DefaultFilterProvider... I thought about using a workaround like that but I was hoping there was a way to do this correctly but it looks like ApiControllerActionSelector is pretty locked down (if that's where the filters are cached). aspnetwebstack.codeplex.com/workitem/277Jennajenne
Z
16

Filters are cached by the WebApi. They should be in transient scope so that it the WebApi can manage the lifecycle. Because of the long lifecycle you can't have any dependency that have a shorter lifecycle.

What you can do though is to create your repository during execution of the filter. For this it is best to inject a factory using the NinjectFactoryExtension:

public class CommunicationKeyValidationFilter : AbstractActionFilter
{
    public CommunicationKeyValidationFilter(IRepositoryFactory repositoryFactory)
    {
        this.repositoryFactory = repositoryFactory;
    }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        var repository = this.repositoryFactory.CreateRepository();
    }
 }

 public interface IRepositoryFactory { IRepository CreateRepository(); }
 kernel.Bind<IRepositoryFactory>().ToFactory();
Zischke answered 15/3, 2013 at 15:46 Comment(4)
Thanks. This isn't as convenient as just using constructor injection but it solves my issue.Jennajenne
@Remo Is this caching of filters specific to WebApi? Or should we use factories for MVC filters as well? Thanks for all your work.Allsun
No it's not specific to WebAPI. Mvc does the sameZischke
This explains my random exceptions. perfect!Miscall

© 2022 - 2024 — McMap. All rights reserved.