[.NETWorld] Database Initialization Strategies in Code-First:

You already created database after running you code first application first time, but what about second time onwards?? Will it create new database every time you run the application? What about production environment? How to alter database when you change your domain model? To handle these scenarios, you have to use one of the database initialization strategies.

 

There are four different database Initialization strategies:

 

  1. CreateDatabaseIfNotExists: This is default initializer. As name suggests, it will create the database if not exists as per the configuration. However, if you change the model class and then run the application with this initializer then it will throw an exception. 
  2. DropCreateDatabaseIfModelChanges: This initializer drops existing database and creates new database if your model classes (entity classes) have been changed. So you don’t have to worry about maintaining your database schema when your model classes changes. 
  3. DropCreateDatabaseAlways: As name suggests, this initializer drops an existing database every time you run the application irrespective of whether your model classes have changed or not. This will be useful when you want fresh database every time you run the application while developing. 
  4. Custom DB Initializer: You can also create your own custom initializer if any of the above doesn’t satisfy your requirement or you want to do some other process when it initialize the database using above initializer. 

 

To use one of the above DB initialization strategies, you have to set the DB Initializer using Database class in Context class as following:

     
    public class SchoolDBContext: DbContext 
    {

        public SchoolDBContext(): base("SchoolDBConnectionString") 
        {
            Database.SetInitializer<SchoolDBContext>(new CreateDatabaseIfNotExists<SchoolDBContext>());

            //Database.SetInitializer<SchoolDBContext>(new DropCreateDatabaseIfModelChanges<SchoolDBContext>());
            //Database.SetInitializer<SchoolDBContext>(new DropCreateDatabaseAlways<SchoolDBContext>());
            //Database.SetInitializer<SchoolDBContext>(new SchoolDBInitializer());
        }
        public DbSet<Student> Students { get; set; }
        public DbSet<Standard> Standards { get; set; }
    }

You can also create your custom DB initializer by inheriting one of the intializer as below:

    
    public class SchoolDBInitializer :  DropCreateDatabaseAlways<SchoolDBContext>
    {
        protected override void Seed(SchoolDBContext context)
        {
            base.Seed(context);
        }
    }

As you can see in the above code, we have created new class SchoolDBInitializer which is derived from DropCreateDatabaseAlways initializer.

 

Set db initializer in the configuration file:

 

You can also set db initializer in the configuration file. For example, to set default initializer in app.config:

   
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <appSettings>
        <add key="DatabaseInitializerForType SchoolDataLayer.SchoolDBContext, SchoolDataLayer"         
            value="System.Data.Entity.DropCreateDatabaseAlways`1[[SchoolDataLayer.SchoolDBContext, SchoolDataLayer]], EntityFramework" />
      </appSettings>
    </configuration>

You can set custom db initializer as following:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <appSettings>    
        <add key="DatabaseInitializerForType SchoolDataLayer.SchoolDBContext, SchoolDataLayer"
             value="SchoolDataLayer.SchoolDBInitializer, SchoolDataLayer" />
      </appSettings>
    </configuration>

So this way you can use DB initialization strategy for your application.

[.NETWorld] 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 registeredfor 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 hashgenerated 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.

[.NETWorld] EF 6.1: Creating indexes with IndexAttribute

Since EF 4.3 it has been possible to use CreateIndex and DropIndex in Code First Migrations to create and drop indexes. However this had to be done manually by editing the migration because the index was not included anywhere in the Code First model. Now with EF 6.1 it is possible to add index specifications to the model such that creating and dropping indexes can be handled automatically by Migrations.

Single column indexes

Consider a simple Blog entity:

1
2
3
4
5
6
7
public class Blog
{
    public int Id { get; set; }
    public string Title { get; set; }
    public int Rating { get; set; }
    public virtual ICollection<Post> Posts { get; set; }
}

Let’s assume this entity is already in our model and migrations have been created and applied so the model and database are both up-to-date. The easiest way to add an index is to place IndexAttribute onto a property. For example, let’s add an index to the column mapped to by the Rating property:

1
2
3
4
5
6
7
8
9
10
public class Blog
{
    public int Id { get; set; }
    public string Title { get; set; }
    [Index]
    public int Rating { get; set; }
    public virtual ICollection<Post> Posts { get; set; }
}

After doing this using Add-Migration will scaffold a migration something like this:

1
2
3
4
5
6
7
8
9
10
11
12
public partial class Two : DbMigration
{
    public override void Up()
    {
        CreateIndex("dbo.Blogs", "Rating");
    }
    public override void Down()
    {
        DropIndex("dbo.Blogs", new[] { "Rating" });
    }
}

The index is being created with a default name and default options. The defaults are as follows:

  • Name: IX_[column_name]
  • Not unique
  • Not clustered

You can also use IndexAttribute to give the index a specific name and options. For example, let’s add a name to the index for the Rating column:

1
2
3
4
5
6
7
8
9
10
public class Blog
{
    public int Id { get; set; }
    public string Title { get; set; }
    [Index("RatingIndex")]
    public int Rating { get; set; }
    public virtual ICollection<Post> Posts { get; set; }
}

Scaffolding another migration for this change results in:

1
2
3
4
5
6
7
8
9
10
11
12
public partial class Three : DbMigration
{
    public override void Up()
    {
        RenameIndex(table: "dbo.Blogs", name: "IX_Rating", newName: "RatingIndex");
    }
    public override void Down()
    {
        RenameIndex(table: "dbo.Blogs", name: "RatingIndex", newName: "IX_Rating");
    }
}

Notice that Migrations has scaffolded a rename for the index from the default name to the new name.

Multiple column indexes

Indexes that span multiple columns can also be scaffolded by using the same index name on multiple properties. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Blog
{
    [Index("IdAndRating", 1)]
    public int Id { get; set; }
    public string Title { get; set; }
    [Index("RatingIndex")]
    [Index("IdAndRating", 2, IsUnique = true)]
    public int Rating { get; set; }
    public virtual ICollection<Post> Posts { get; set; }
}

Notice that the order of columns in the index is also specified. The unique and clustered options can be specified in one or all IndexAttributes. If these options are specified on more than one attribute with a given name then they must match.

Scaffolding a migration for this change results in:

1
2
3
4
5
6
7
8
9
10
11
12
public partial class Four : DbMigration
{
    public override void Up()
    {
        CreateIndex("dbo.Blogs", new[] { "Id", "Rating" }, unique: true, name: "IdAndRating");
    }
    public override void Down()
    {
        DropIndex("dbo.Blogs", "IdAndRating");
    }
}

Index conventions

The ForeignKeyIndexConvention Code First convention causes indexes to be created for the columns of any foreign key in the model unless these columns already have an index specified using IndexAttribute. If you don’t want indexes for your FKs you can remove this convention:

1
2
3
4
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Remove<ForeignKeyIndexConvention>();
}

What IndexAttribute doesn’t do

IndexAttribute can be used to create a unique index in the database. However, this does not mean that EF will be able to reason about the uniqueness of the column when dealing with relationships, etc. This feature usually referred to as support for “unique constraints” which can be voted for as a feature suggestion and on the CodePlex work item.