DbContext Disposed after first request when using Ninject's InRequestScope()
Asked Answered
O

3

5

I am new to both EF and Ninject so forgive me if this does not make sense :)

I have an MVC3 application with the Ninject and Ninject.Web.Common references. I am trying to inject a DbContext into my repositories. What I am seeing is that on the first request, everything works wonderfully but the subsequent requests return:

System.InvalidOperationException: The operation cannot be completed because the DbContext has been disposed.
   at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
   at System.Data.Entity.Internal.Linq.DbQueryProvider.Execute[TResult](Expression expression)
   at System.Linq.Queryable.SingleOrDefault[TSource](IQueryable`1 source, Expression`1 predicate)

My bindings:

kernel.Bind<ISiteDataContext>().To<SiteDataContext>().InRequestScope();
kernel.Bind<IProductRepository>().To<ProductRepository>();
kernel.Bind<IProductService>().To<ProductService>();

My Service class:

public class ProductService : IProductService {
    [Inject]
    public IProductRepository repository {get; set;}

    ...
}

My Repository class:

public class ProductRepository : IProductRepository {
    [Inject]
    public ISiteDataContext context {get; set;}

    ...
}

My SiteDataContext class:

public class SiteDataContext  : DbContext, ISiteDataContext 
{
    static SiteDataContext()
    {
        Database.SetInitializer<SiteDataContext >(null);
    }

    public DbSet<Product> Products{ get; set; }


    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
    }
}

My controller:

public class ProductController {
    [Inject]
    public IProductService productService {get; set;}

    ...
}

If I remove .InRequestScope(), then it works fine - but then that causes problems with Entity Framework since objects are modified in multiple separate instances of the data context.

Outgo answered 22/6, 2012 at 3:28 Comment(0)
O
5

Naturally, soon after posting something clicked in my mind, and I was able to solve this.

The problem lies in the fact that the behavior of ActionFilters were changed in MVC3 and I had a filter that had my ProductService injected.

I suppose that the filter disposed of the service and that eventually disposed of the DbContext.

In my case, the solution was easy. I created a second DbContext that is used specifically for my filter. Since the filter does nothing more than query a select few tables to verify authorization to specific resources, I did not need the Unit of Work context that DbContext provides across a single request. I created a new service that uses the new DbContext. In this case, it is sufficient to be configured with InTransientScope()

Outgo answered 22/6, 2012 at 3:50 Comment(0)
X
6

Set your repositories to be InRequestScope as well. They should dispose after each request.

Also with MVC you should be using constructor injection to inject your repository into your controller instance as well.

Xiomaraxiong answered 22/6, 2012 at 3:47 Comment(3)
Is there a benefit to constructor injection vs attribute injection?Outgo
Absolutely, it adheres to the composition root. Use attributes for this pattern isn't appropriate for several reasons. Using constructor injection works fine here, and it lets dependencies known at the earliest possible time and since there is no reason to have this as an optional dependency with a default here it is the preferred method. See manning.com/seemann which is the best book imho on the subject.Xiomaraxiong
I am using DependencyResolver.Current.GetService< ... instead of costructor injection. Is it possible that this is causing the same issue? (The operation cannot be completed because the DbContext has been disposed)Beera
O
5

Naturally, soon after posting something clicked in my mind, and I was able to solve this.

The problem lies in the fact that the behavior of ActionFilters were changed in MVC3 and I had a filter that had my ProductService injected.

I suppose that the filter disposed of the service and that eventually disposed of the DbContext.

In my case, the solution was easy. I created a second DbContext that is used specifically for my filter. Since the filter does nothing more than query a select few tables to verify authorization to specific resources, I did not need the Unit of Work context that DbContext provides across a single request. I created a new service that uses the new DbContext. In this case, it is sufficient to be configured with InTransientScope()

Outgo answered 22/6, 2012 at 3:50 Comment(0)
C
0

I'd argue against putting DbContext in a RequestScope because according to the NInject documentation, RequestScope relies on HttpContext. That's not guaranteed to be disposed at the end of your request.

I once experimented with putting DbContext in various object scopes, but always seemed to get inconsistent results.

The Ninject kernel maintains a weak reference to scoping objects and will automatically Dispose of objects associated with a scoping object when the weak reference to it is no longer valid. Since InRequestScope() uses HttpContext.Current or OperationContext.Current as the scoping object, any associated objects created will not be destroyed until HttpContext.Current or OperationContext.Current is destroyed. Since IIS/ASP.NET manages the lifecycle of these objects, disposal of your objects is tied to whenever IIS/.NET decides to destroy them and that may not be predictable.

Chrysoberyl answered 3/10, 2018 at 20:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.