Code First: Inside DbContext Initialization

A lot of stuff happens when you use a DbContext instance for the first time. Most of the time you don’t worry about this stuff, but sometimes it’s useful to know what’s happening under the hood. And even if it’s not useful, it’s hopefully interesting for its geek value alone.

Note that even though there is a lot of detail below I’ve actually simplified things quite a lot to avoid getting totally bogged down in code-like details. Also, I’m writing this from memory without looking at the code so forgive me if I forget something. :-)

Creating a DbContext instance

Not very much happens when the context instance is created. The initialization is mostly lazy so that if you never use the instance, then you pay very little cost for creating the instance.

It’s worth noting that SaveChanges on an un-initialized context will also not cause the context to be initialized. This allows patterns that use auto-saving to be implemented very cheaply when the context has not been used and there is therefore nothing to save.

One thing that does happen at this stage is that the context is examined for DbSet properties and these are initialized to DbSet instances if they have public setters. This stops you getting null ref exceptions when you use the sets but still allows the sets to be defined as simple automatic properties. The delegates used to do this are cached in a mechanism similar to the one described here.

DbContext initialization

The context is initialized when the context instance is used for the first time. “Use” means any operation on the context that requires database access or use of the underlying Entity Data Model (EDM). The initialization steps are:

  1. The context tries to find a connection or connection string:
    1. If a DbConnection was passed to the constructor, then this is used.
    2. Else, if a full connection string was passed, then this is used.
    3. Else, if the name of a connection string was passed and a matching connection string is found in the config file, then this is used.
    4. Else, the database name is determined from the name passed to the constructor or from the context class name and the registered IConnectionFactory instance is used to create a connection by convention.
  2. The connection string is examined to see if it is an Entity Framework connection string containing details of an EDM to use or if it is a raw database connection string containing no model details.
    1. If it is an EF connection string, then an underlying ObjectContext is created in Model First/Database First mode using the EDM (the CSDL, MSL, and SSDL from the EDMX) in the connection string.
    2. If it a database connection string, then the context enters Code First mode and attempts to build the Code First model as described in the next section.

I made a post on the EF Team blog that describes some of the connection handling in more detail.

Building the Code First model

The EDM used by Code First for a particular context type is cached in the app-domain as an instance of DbCompiledModel. This caching ensures that the full Code First pipeline for building a model only happens once when the context is used for the first time. Therefore, when in Code First mode:

  1. DbContext checks to see if an instance of DbCompiledModel has been cached for the context type. If the model is not found in the cache, then:
    1. DbContext creates a DbModelBuilder instance.
      1. By default, the model builder convention set used is Latest. A specific convention set can be used by setting the DbModelBuilderVersionAttribute on your context.
    2. The model builder is configured with each entity type for which a DbSet property was discovered.
      1. The property names are used as the entity set names, which is useful when you’re creating something like an OData feed over the model
    3. The IncludeMetadataConvention convention is applied to the builder. This will include the EdmMetadata entity in the model unless the convention is later removed.
    4. The ModelContainerConvention and ModelNamespaceConvention are applied to the builder. These will use the context name as the EDM container name and the context namespace as the EDM namespace. Again, this is useful for services (like OData) that are based on the underlying EDM.
    5. OnModelCreating is called to allow additional configuration of the model.
    6. Build is called on the model builder.
      1. The model builder builds an internal EDM model representation based on configured types and reachability from those types and runs all the Code First conventions which further modify the model/configuration.
        1. The connection is used in this process since the SSDL part of the model depends on the target database, as represented by the provider manifest token.
    7. Compile is called on the DbModel to create a DbCompiledModel. DbCompiledModel is currently a wrapper around the MetadataWorkspace.
      1. The model hash is also created by the call to compile.
    8. The DbCompiledModel is cached.
  2. The DbCompiledModel is used to create the underlying ObjectContext instance.

Database initialization

At this point we have an underlying ObjectContext, created either through Code First or using the EDM in the connection string.

DbContext now checks whether or not database initialization has already happened in the app-domain for the type of the derived DbContext in use and for the database connection specified. If initialization has not yet happened, then:

  1. DbContext checks whether or not an IDatabaseInitializer instance has been registered for the context type.
    1. If no initializer (including null) has been explicitly registered then a default initializer will be automatically registered.
      1. In Code First mode, the default initializer is CreateDatabaseIfNotExists.
      2. In Database/Model First mode, the default initializer is null, meaning that no database initialization will happen by default. (Because your database almost always already exists in Database/Model First mode.)
  2. If a non-null initializer has been found, then:
    1. A temporary ObjectContext instance is created that is backed by the same EDM as the real ObjectContext. This temp is used by the DbContext instance for all work done by the initializer and then thrown away. This ensures that work done in the initializer does not leak into the context later used by the application.
    2. The initializer is run. Using the Code First default CreateDatabaseIfNotExists as an example, this does the following:
      1. A check is made to see whether or not the database already exists.
      2. If the database does not exist, then it is created:
        1. This happens through the CreateDatabase functionality of the EF provider. Essentially, the SSDL of the model is the specification used to create DDL for the database schema which is then executed.
          1. If the EdmMetadata entity was included in the model, then the table for this is automatically created at the same time since it is part of the SSDL just like any other entity.
        2. If the EdmMetadata entity was included in the model, then the model hash generated by Code First is written to the database by saving an instance of EdmMetadata.
        3. The Seed method of the initializer is called.
        4. SaveChanges is called to save changes made in the Seed method.
      3. If the database does exist, then a check is made to see if the EdmMetadata entity was included in the model and, if so, whether there is also a table with a model hash in the database.
        1. If EdmMetadata is not mapped or the database doesn’t contain the table, then it is assumed that the database matches the model. This is what happens when you map to an existing database, and in this case it is up to you to ensure that the model matches the database. (Note DropCreateDatabaseIfModelChanges would throw in this situation.)
        2. Otherwise, the model hash in the database is compared to the one generated by Code First. If they don’t match, then an exception is thrown. (DropCreateDatabaseIfModelChanges would drop, recreate, and re-seed the database in this situation.)
    3. The temporary ObjectContext is disposed.
  3. Control returns to whatever operation it was that caused initialization to run.

That’s the basics. Like I mentioned above, I missed some details intentionally, and I probably missed some more by mistake. Hopefully it was somewhat useful/interesting anyway.

Thanks for reading!
Arthur

P.S. There is an alternate theory of how DbContext works that suggests nuget introduces a herd of unicorns into your machine which then run on treadmills to create magic entity juice that in turn magically connects your objects to your database. I cannot comment on this theory without breaking confidentiality agreements I have signed with the unicorn king. Or something.

About Arthur Vickers

Developer on the Entity Framework team at Microsoft.
This entry was posted in Code First, Database Initializers, DbContext API, Entity Framework and tagged , , . Bookmark the permalink.

26 Responses to Code First: Inside DbContext Initialization

  1. Really interesting!
    Thanks

  2. René says:

    Thanks for the background info, very helpful.

  3. Pingback: Associations in EF 4.1 Code First: Part 6 – Many-valued Associations - Enterprise .Net

  4. Greg Foote says:

    Arthur,

    Nice Post !

    I am currently running into an issue where during the ‘Build’ portion of the Model code first is generating an exception in this method:

    NavigationPropertyConfiguration.ValidateConsistency

    It appears that the model is built on background threads ? I cannot get any information about the exception that is thrown about which Entity / Navigation Property is causing the exception…

    What is the best way to debug this ?

    Thanks
    Greg

    • @Greg EF doesn’t start up any background threads. However, building the model is something that only happens once in one thread, so if you have multiple threads making requests through different context instances then only one of these threads will actually build the model. To debug you could try using your model in a simple console app where there will only ever be one application thread.

  5. Greg Foote says:

    Arthur,

    I had to install Reflector and Decompile the EF Assembly to find out what the problem was:

    This is the line that causes the problem…

    private void ValidateConsistency(NavigationPropertyConfiguration navigationPropertyConfiguration)
    {
    if ((navigationPropertyConfiguration.InverseEndKind.HasValue && this.EndKind.HasValue) && (navigationPropertyConfiguration.InverseEndKind != this.EndKind))
    {
    throw System.Data.Entity.ModelConfiguration.Resources.Error.ConflictingMultiplicities(this.NavigationProperty.Name, this.NavigationProperty.ReflectedType);
    }
    if ((navigationPropertyConfiguration.EndKind.HasValue && this.InverseEndKind.HasValue) && (navigationPropertyConfiguration.EndKind != this.InverseEndKind))
    {
    throw System.Data.Entity.ModelConfiguration.Resources.Error.ConflictingMultiplicities(this.InverseNavigationProperty.Name, this.InverseNavigationProperty.ReflectedType);
    }

    In my case the InverseNavigationProperty property is null which causes and exception WHEN EF is trying to throw an exception…

    You guys may want to handle this method with a try catch or something to be able to give the user some more meaningful info so they don’t have to purchase Reflector

    Thanks !!
    Greg

  6. Greg Foote says:

    Arthur,

    I fixed the error on my side… and I honestly con’t remember which entities were involved… But I do remember that the issue was an optional 1 to 1 navigation property that had no mapping explicitly configured…

    I got really behind on this project trying to find the cause of my incorrect mapping (We have 130+ entities in our model) so I have to hussle to catch up with the team or I would stop and try and re-create the issue in a repro project for you…

    If I run into something similar again I can definitely do a repro !

    Thanks
    Greg

  7. RDelanhese says:

    Hello, I have a database schema existing, but I would like to use the fluent syntax to mapping this instead of using pre-generated EDM XML File. So I’ve created a custom DataBaseInitializer that sets the connection string for my context. Something like this:

    public void InitializeDatabase(Contexto context)
    {

    context.Database.Connection.ConnectionString = @”Data Source=SRV01;Initial Catalog=Northwind; User Id = sa;Password=xxx”;

    context.Database.CreateIfNotExists();

    }

    and this

    Database.SetInitializer(new Initializer());

    But the InitializeDataBase method is never called.

    Some idea of what is happening??

  8. parkster says:

    I think this post may have just saved me! Having a problem with ef codefirst calls taking a long time to run…ProxyCreation is disabled and calling from wcf, from our trace logs it appears the model is being recreated every time rather than being cached….not sure why yet but this post has helped my understanding a huge amount…was feeling rather uncomfortable with the black box approach as well so thanks a lot…

  9. asdf says:

    I love “P.S” section :)

  10. José says:

    First of all thanks for taking time to write such a complex blog about EF. I found your blog while I was looking a way to cache the DbCompiledModel. The problem is that my model has hundred of entities (which takes several seconds to build) and I would rather create the model before deployment, rather than each time an app-domain is started. Is there any way to achieve this? thanks.

  11. Hi José,

    The only way to do this currently is to generate an EDMX and then have EF load that EDMX instead of building the model from code each time. You can generate the EDMX from a Code First model using the EdmxWriter class. I believe that the EF Power Tools can also do this. Once you have an EDMX you can add it to your VS project as an existing item and then you would reference it using a “Database First”-style EF connection string. Hopefully you can figure out the details. Let me know if you get stuck.

    The other thing to check is that it is the model building that is taking the time and not view generation. You might want to look into using pre-generated views if you haven’t already.

    Thanks,
    Arthur

    • José says:

      Arthur,
      Thanks a lot for your answer. I did manage to use the EdmxWriter to generate the model and the Power Tools to generate the views. the speed has gone up, now its significantly faster. One more question, when I used the power tools i got a DBContext.Views.cs, That file is automatically used? or i am missing some steps?.

      Arthur once again many thanks, if it wasn’t for your answer i would have spent the rest of the week searching.

      Happy new year.

  12. Steve says:

    Hi. I’m having an issue whereby everything works fine in my environment until I make a model change. Prior to the change (just adding a field to an entity) it creates the DB, creates the tables, runs the Seed method and all of the migrations. If I make a change (add or remove a single column) it creates the DB, creates the table, runs the migrations but doesn’t run the Seed. Both instances I have done the same thing i.e. executed a web service which interacts with the DB. Do you have any idea why my model seems broken if I modify it, or how to resolve? Thanks Steve

    • @Steve If you are using Migrations, then you should use the Seed method in your Migrations configuration class. If you are not using Migrations then which database initializer are you using?

      • Steve says:

        Hi. I am using migrations, and am using the MigrateDatabaseToLatestVersion initializer for my context. I have found that if I drop the DB, make a change to the model, then execute the wcf service which interacts with the DB then the structure of the DB is created, but the Seed method is not (ever) executed. This is true however many times I execute the service. If I then do an IISRESET and re-execute the service, the very first invocation of the service causes the Seed method to run! This behaviour has changed over the last few weeks and for the life of me I cannot work out what has changed; before it was the case that I could just drop the DB and execute the service and it created the DB, ran the migrations and performed the seed first time. I have (today) rebuilt a new VM from scratch and pulled the code down to figure out if it was an environmental issue but I have just discovered that it’s not as the new VM is exhibiting the same issues… Any idea?

      • @Steve Can you please file a bug on entityframework.codeplex.com? Please include any info (code, etc.) that could help us repro the problem.

  13. Eric Murchie-Beyma says:

    Hi, I am in a double bind, trying to construct a DbContext on an open connection. I have two Code First modules in my application, each with a DbContext that models different subsets of the same database (so they have completely different entity models). I want module A to begin a transaction, make some changes, then call module B, which will make more changes in the same transaction. When module A calls module B, it passes its ObjectContext.Connection, and module B passes this to its DbContext constructor. The problem is that module B is apparently in Model First mode, and does not construct its own model. It seems to be reusing module A’s model. As a result, references to module B’s DbSets generate the error, “The entity type is not part of the model for the current context.” I tried passing module A’s context.Database.Connection, to force Code First mode, and got the error, “EntityConnection can only be constructed with a closed dbconnection.”
    Is there any way to accomplish what I’m trying to do?

  14. @Eric I’ve given this a bit of thought and tried a few things. The bad news is that I can’t get this to work with EF5 because of known issues with the connection and transaction design. The good news is that I was able to make this work quite easily with EF6. This is because EF6 allows a DbContext to be constructed with an already open connection and the new BeginTransaction and UseTransaction methods on DbContext.Database allow both contexts to opt-in to the same transaction.

    With EF5 the closest I was able to get to is:
    - Create an ambient transaction using System.Transactions. I could not make it work with a DbTransaction.
    - Create both DbContexts before the connection is opened.
    - After creating both contexts obtain the ObjectContext for both connections
    - After obtaining both ObjectContexts call open on both ObjectContext.Connection properties.
    Both DbContexts will now be sharing the same connection and ambient transaction.

    • Eric Murchie-Beyma says:

      Arthur,
      Thanks very much for your answer. It worked beautifully on both versions. Your EF5 solution led me to realize that with System.Transactions, the transaction doesn’t NEED to ride along with the connection. So I arrived at an even simpler (and yes, textbook) solution:
      using (TransactionScope trans = new TransactionScope())
      {
      using (ModuleAContext context = new ModuleAContext(this.ConnectionString))
      {
      context.TableAList.Add(new TableA{ Column1 = “ModuleA” });
      context.SaveChanges();
      }

      using (ModuleBContext context = new ModuleBContext(this.ConnectionString))
      {
      context.TableBList.Add(new TableB { Column1 = “ModuleB” });
      context.SaveChanges();
      }
      trans.Complete();
      }
      Thanks again for your help.

  15. prosperva says:

    Recently my database got deleted and this was a message I got form the IT team:

    We saw in the trace logs (the bottom two lines in the screenshot below) that an Object:Deleted event occurred on the xxxx database executed by EntityFrameworkMUE (the SessionLoginName was username) from HostName yyyyyy.`

    Migrations on the project are disabled and I was not using any of the initializers. What would have caused this?

    Thanks

  16. Ben says:

    I believe a group of unicorns is called a blessing :)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s