30

Looking at my Elmah error logs, I am seeing a few InvalidOperationExceptions from Entity Framework that deal with:

The context cannot be used while the model is being created.

This is with the latest EF CodeFirst library from Nuget. The only information I have been able to find on the net is that it is being caused by having data contexts as singletons, which is most certainly not my case. In my Windsor installer, my EF unit of work structure is being registered with:

container.Register(Component.For<IUnitOfWork>()
                            .ImplementedBy<EFUnitOfWork>()
                            .LifeStyle
                            .PerWebRequest);

I am able to recreate the error by hitting F5 in VS to start a debugging sessions, and while IIS is spinning up load up a second webpage to the debug session.

I suspect it is because the user is trying to access the system while Asp.net has unloaded due to the lack of activity, which makes sense as my product is currently in a very very small beta test. However, since real people are using the website with live data, I need as little errors occurring as possible.

Does anyone have any idea how to prevent this from occurring?


Edit: I updated my windsor controller to now contain the following code:
        container.Register(Component.For<IUnitOfWork>().ImplementedBy<EFUnitOfWork>().LifeStyle.PerWebRequest);
        using (var context = new MyJobLeadsDbContext())
        {
            context.Set<UnitTestEntity>().Any();
        }

However, when I attempt to perform a 2nd web request while IIS is loading the application, the previous error still occurs


Edit 2: As requested, here is the stack
   at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
   at System.Data.Entity.Internal.InternalContext.Initialize()
   at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)
   at System.Data.Entity.Internal.Linq.InternalSet`1.Initialize()
   at System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext()
   at System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Provider()
   at System.Linq.Queryable.Where[TSource](IQueryable`1 source, Expression`1 predicate)
   at MyApp.DomainModel.Queries.Users.UserByEmailQuery.Execute() in C:\Users\KallDrexx\Documents\Projects\MyApp\MyApp.DomainModel\Queries\Users\UserByEmailQuery.cs:line 44
   at MyApp.Infrastructure.MyAppMembershipProvider.GetUser(String email, Boolean userIsOnline) in C:\Users\KallDrexx\Documents\Projects\MyApp\MyApp\Infrastructure\MyAppMembershipProvider.cs:line 102
   at System.Web.Security.Membership.GetUser(String username, Boolean userIsOnline)
   at System.Web.Security.Membership.GetUser()
   at MyApp.MyAppBaseController.Initialize(RequestContext requestContext) in C:\Users\KallDrexx\Documents\Projects\MyApp\MyApp\MyAppBaseController.cs:line 23
   at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext)
   at System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext)
   at System.Web.Mvc.MvcHandler.<>c__DisplayClass6.<>c__DisplayClassb.<BeginProcessRequest>b__5()
   at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass1.<MakeVoidDelegate>b__0()
   at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End()
   at System.Web.Mvc.MvcHandler.<>c__DisplayClasse.<EndProcessRequest>b__d()
   at System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f)
   at System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action)
   at System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult)
   at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
KallDrexx
  • 27,229
  • 33
  • 143
  • 254
  • This is interesting. Can find more details about the problem? Once the model is created your thread processing the request should be blocked so the context should not be in use. – Ladislav Mrnka May 23 '11 at 16:09
  • I don't know how to find more details about this. After visual studio's debugging thread finishes loading, I no longer see this error. It only seems to occur at the very first load of the webpage if two simultaneous calls occur at the same time. – KallDrexx May 23 '11 at 16:23
  • Two simultaneous calls? Can you validate that you have new instance of the context for each request? – Ladislav Mrnka May 23 '11 at 16:46
  • hybrid life style can be helpful in multitasking scenario http://stackoverflow.com/questions/7203745/windsor-composite-lifestyle-for-asp-net-process – Søren Sep 01 '13 at 07:09
  • 1
    You can receive this error if the DB is not accessible as well (e.g. not started). – Matthew Oct 10 '13 at 16:04
  • I faced very same error. I had this unitOfWork class and class ABC. Class ABC was using dbContext object through unitOfWork class. Both class ABC and unitOfWork were getting instantiated through windsor castle DI container. Registration of UnitOfWork class was exactly the same as yours i.e. LifeStyle.PerWebRequest but the issue was in the way I had registered class ABC in the initialization of DI container. I had written code like Register(Component.For().ImplementedBy() **.LifeStyle.Transient** ). I had missed the code in bold to end up with this issue. – RBT Feb 12 '16 at 19:24

3 Answers3

17

I finally figured out the true cause of this, at least for me.

The issue was that I was retrieving a DbContext from Windsor in my custom Asp.Net Membership provider. This caused an issue because the membership provider has a lifespan of the whole application, while all other retrieval calls for the db context were new db contexts for the specific web requests. This meant that two database contexts were "spinning up" at the same time and thus this error was thrown.

This also caused a lot of hard to debug entity caching issues as well, so anyone who uses EF in their membership provider needs to be real careful about their context lifetime.


Edit: In response to DotNetWise, I solved this by forcing my custom membership provider to always use an EF connection from Windsor by storing the Windsor connection factory in my constructor, then always retrieving my EF data context from the factory at that point.

For example:

public class CustomMembershipProvider : MembershipProvider
{
    private IServiceFactory _serviceFactory;

    public CustomMembershipProvider() : this(null) { }

    public CustomMembershipProvider(IServiceFactory factory)
    {
        // IF no factory was provided, we need to get one from the bootstrapper
        if (factory == null)
            _serviceFactory = new WindsorServiceFactory(Bootstrapper.WindsorContainer);
        else
            _serviceFactory = factory;
    }

    public override string ResetPassword(string email, string answer)
    {
        var unitOfWork = GetUnitOfWork();
        return new ResetUserPasswordCommand(unitOfWork).WithUserEmail(email).Execute();
    }

    private IUnitOfWork GetUnitOfWork()
    {
       return _serviceFactory.GetService<IUnitOfWork>();
    }
}

The idea being that any action the membership provider performs gets the UnitOfWork class from Windsor, and uses that to perform the action (in this case my UnitOfWork class is a repository holder to wrap up my EF data context)

KallDrexx
  • 27,229
  • 33
  • 143
  • 254
  • 3
    Sorry I should have included that. I solved it by making EVERY one of my membership provider methods make a new request to Windsor for my DbContext, and never used the same DBContext outside that method. Prior to that I was requesting the DbContext via my membership provider's constructor and was storing it for later keeping, which caused the issue. – KallDrexx May 23 '12 at 11:34
  • 1
    Sorry to bother you guys. It was failing because of wrong attribute on one of the properties of my POCO classes [Column("ToDoItem", TypeName = "NVARCHAR(2000)")] works. Length of varchar and nvarchar data types are required to be mentioned explicitly through stringLength attribute. It can't be mentioned in-line inside Column attribute as long as it is not MAX length. – RBT May 09 '16 at 00:40
7

I ran into the same issue in a multithreaded WPF app.

My workaround was to force DbContext initialization from the Windsor installer:

container.Register(Component.For(TheDbContext.Blah.Blah));
using (var context = new TheDbContext())
      context.Set<SomeRandomEntity>().Any();

I might add in my opinion this qualifies as a bug in EF: they should have used thread-safe (with locks, or whatever) code for DbContext initialization.

Of course, a better solution is what NHibernate does: the SessionFactory is an explicitly created, separate object from the Session.

Diego Mijelshon
  • 52,548
  • 16
  • 116
  • 154
  • Calling `container.Resolve()` and making a DB call does not stop this issue from happening unfortunately. I agree with you that EF should be thread-safe for initialization, and it's really annoying that it's not. – KallDrexx May 23 '11 at 23:45
  • @KallDrexx: it does if the container initialization itself is done in a thread-safe way. I'm not even using resolve; that code is part of my `DbInstaller` (a class implementing `IWindsorInstaller`) – Diego Mijelshon May 24 '11 at 00:35
  • Ok I tried not using my unit of work and interacting with the `DbContext` itself. Now my 2nd request dies with `The underlying provider failed on Open.` on the same line of code, so this still doesn't seem to work for me :( – KallDrexx May 24 '11 at 01:26
  • @KallDrexx: post the full exception with stack trace. – Diego Mijelshon May 24 '11 at 01:59
  • Well I can't get that error to happen again, but the original error now occurs each time. I posted my new windsor installer code in the main question – KallDrexx May 24 '11 at 02:25
  • I have posted the full stack trace of the `he context cannot be used while the model is being created.` exception – KallDrexx May 25 '11 at 00:25
  • @KallDrexx: where/when are you initializing the container? – Diego Mijelshon May 25 '11 at 10:39
  • First of all, thanks for still coming back to this :). After looking for the answer to this I think I may have an idea of why this is happening. My Windsor container is being initialized in a class that is marked with `[assembly: WebActivator.PostApplicationStartMethod (typeof(Bootstrapper), "Wire")]`, thus it's not initializing the container until after the app starts. I am wondering if I change this to `PreApplicationStartMethod` if that will make this issue go away. I'll give it a try when I get home! – KallDrexx May 25 '11 at 12:21
  • @KallDrexx: trying can't hurt... I'm pretty sure about why the exception occurs, it's just a matter of finding out how to force initialization to be complete before the context is accessed again. – Diego Mijelshon May 25 '11 at 16:06
  • Well that's why I'm thinking `PreApplicationStartMethod` should work, because that *should* cause the installer to run (with your suggested addition of calling the `DbContext`) prior to the application fully starting, and thus the 2nd web request may not execute until after the context is fully up and running (therefore not causing this exception to occur) – KallDrexx May 25 '11 at 16:39
  • @KallDrexx: You should log debug information to confirm if the second request is being executed before initialization is complete. – Diego Mijelshon May 26 '11 at 03:20
  • I just forced container to init itself like Diego and everything was right, the different is I used a fake query : `dbcontext.Set().Select(q => q.Id)` – Søren Aug 31 '13 at 11:12
3

When i encountered this issue, i found it was a dbconnection gone wrong.

I corrected my EntityFramework dbconnection string and all was fine

Oladipo Olasemo
  • 2,010
  • 24
  • 31