5
votes

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.

3

3 Answers

6
votes

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.

5
votes

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()

0
votes

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.