I’ve been exploring the recent CTP5 release of .NET Entity Frameworks 4.0. I find the advancements in the “Code First” model of entity modeling encouraging, though I am a little concerned about discoverability. If things just magically happen when you name your properties just the right way, how will you know how to diagnose and fix it when the magic smoke leaks out?
(You do know that computers run on smoke, right? ‘Cause if you let the smoke out, the computer don’t work no more!)
My experiments with Code First took a turn for the worse today as my test app deteriorated from working nicely to failing with murky error messages that seemed to have little to do with the source code changes I just made. How am I supposed to figure this stuff out if they keep rearranging the maze?
I had configured my domain service to always drop the database tables and reset the tables with test data (using DropCreateDatabaseAlways) whenever the entity model changed. I knew I would be making lots of changes to the model/schema, so might as well just wipe the slate clean every time.
As the strange failures began to mount, I noticed that sometimes after modifying my POCO entity classes (and therefore changing the entity model) running the test app did not hit my breakpoints in the context’s OnModelCreating or the initializer’s Seed methods.
To figure out what I had changed to cause all this pain, I started commenting out non-essential parts of the POCO classes, eliminating associations and composite keys and so forth. The weird failures continued. The failures weren’t even consistent – sometimes the client app would run once just fine and create all the tables it needed, then fail mysteriously with an invalid metadata error on the second run or when the page was refreshed. Sometimes the first run didn’t even survive the first web request data query.
By the time I had whittled the POCO classes down to a single class with no inheritance and no associations and still had the client fail, I decided it was time to take a lunch break. It didn’t matter what time it was, I needed to put it aside, leave it alone for awhile so I could make a fresh start later.
When I came back from my lunch break there was an error message box on the screen that I hadn’t seen before. It said the database could not be dropped because it was in use by another client. I had left the debugger paused at a breakpoint in the test app and it had trapped this exception some time after I had left my desk.
After a moment of head scratching, I realized the other database client was the Server View in Visual Studio that I was using to inspect the tables created by EF to see how my changes to the POCO entities affected the SQL schema. Close the database connection in VS, restart the app and poof! Everything worked as it should. It even worked twice in a row, which at that point was a new record for the day.
What was happening was the domain service in my test app was actually attempting to drop the tables from the database and build everything anew, but Visual Studio’s open connection to the SQLExpress service was blocking the drop action. Apparently, dropping the tables is a asynchronous action with a very long timeout interval. The client would make its first request for data, the domain service would receive the request and fire up the DbContext, realize that the model and database needed to be regenerated, and issue the database drop and create operation.
The drop and create didn’t complete immediately, but apparently other activities proceeded asynchronously and began to fail because the schema found in the database didn’t match the expected schema. I would see these bizarre failure error messages while stepping through the domain service code as part of the initial data request. What I didn’t see was the root cause – the drop tables failure – because it didn’t fail until long after I had killed the test app and gone back to wondering what I was doing wrong in my source code.
So, simple solution: Always close any open connections in the Server Explorer before running an app that will drop tables.
This is not the error you’re looking for
Another tip for working with Code First Entities: if you get a compile error about “You can’t use this entity in a method of your domain service class because it has not been exposed in your DbContext”, but you look at your DbContext class and you can see you most certainly have exposed that entity as a DbSet<class> property, here’s what you do: Ignore the error message. It’s a red herring. Instead, go check that your entity class explicitly designates a property as the primary key, using the [Key] attribute. Fluid API calls to define a key in OnModelCreate won’t do – VS will only be satisfied by a Key attribute.
I don’t claim to know why, but after a day of permutations and combinations, this is what I found cleared the problem for me.