Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple Injector “The operation cannot be completed because the DbContext has been disposed” during MVC validation

I have a pretty complicated problem. I'm using FluentValidation.MVC in my ASP.NET MVC 5 project. In it, I use a repository pattern to check whether a user's email address is not duplicated. The issue isn't really the repository pattern; it's that the repository uses an Entity Framework context injected into the constructor at run time:

public class SomeRepository
{
  //IEFContext is something I modified the T4 template to generate
  public SomeRepository(IEFContext context)
  {
    _context = context;
  }
}

The application uses this approach and it works great. SimpleInjector wires up my EF context, which is scoped to use the Per ASP.NET web request (wraps around HttpContext.Items).

Dim httpLifecycle = New SimpleInjector.Integration.Web.WebRequestLifestyle(True)
container.Register(of IEFContext, EFContext)(httpLifecycle)

No issues with the app here, just the validation. When the server receives the post operation, the error I'm getting is "The operation cannot be completed because the DbContext has been disposed". It appears I cannot use any EF-related code in a FluentValidation attribute, which uses an EF context on a per web request basis. Nothing special about the validation attribute, which does:

public class Val : AbstractValidator<Entity>
{
   public Val()
   {
       _repos = Container.GetInstance<ISomeRepos>();
       RuleFor(i => i.Email).Must((o, v) =>
       {
           _repos.HasDistinctEmail(o.ID, v);
       }
   }
}

The context should have died with the previous request since stored in HttpContext.Items. Any idea what's going on? I know by setting True to the WebRequestLifecycle, I am causing the EF context to be disposed on request end. I would think that would be desirable.

like image 282
Brian Mains Avatar asked Sep 28 '22 13:09

Brian Mains


1 Answers

My best bet is that an instance of the Val class is cached for the duration of the AppDomain (a singleton), which means that its constructor is called just once and therefore it only resolves one single ISomeRepos, causing that repo to be promoted to singleton as well (and with it, all its dependencies).

The quick fix is simple, move the GetInstance call inside the delegate:

public Val()
{
    RuleFor(i => i.Email).Must((o, v) =>
    {
        repos = Container.GetInstance<ISomeRepos>();
        repos.HasDistinctEmail(o.ID, v);
    }
}
like image 130
Steven Avatar answered Oct 04 '22 23:10

Steven