Charlie Kilian Charlie Kilian - 2 months ago 19
C# Question

How do I prevent EF CodeFirst from trying to run migrations on my database, when configuring dependency injection with Unity?

How do I prevent CodeFirst from trying to run migrations on my database?

I am getting

SqlException
thrown with the following error message:

Invalid object name 'dbo.__MigrationHistory'.


These exceptions are being caught and logged by my logging framework. I need to prevent the exception from happening so lots of spurious log messages aren't generated by my application.

I understand the
__MigrationHistory
table is part of Code First Migrations. I do use Code First, but I do not use its migrations. (The project uses FluentMigrator instead.) According to this blog post and this answer, to disable the error, I need to do the following:

static InstitutionContext()
{
System.Data.Entity.Database.SetInitializer<InstitutionContext>(null);
}


(This, of course, for a
DbContext
object named
InstitutionContext
.)

I have done this, and verified that the line of code is getting hit. However, I am still getting the error in the logs.

This answer explains what is happening, and I think it is basically correct. In short, EF is probing the
__MigrationsHistory
table, throws an exception when it doesn't find it, and then continues without doing anything with migrations. But the answer does not explain how to suppress the exception from happening.

How do I prevent the EF Code First migrations from trying to run and throwing its
SqlException
?

UPDATE 1

I tried configuring the setting via the App.config/Web.config file, and that didn't work either. I also tried moving it from a static method on my
DbContext
to its own configuration class, like this:

public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetDatabaseInitializer<InstitutionContext>(null);
}
}


The class is in the same assembly as the
DbContext
like it's supposed to be. This didn't work either.

UPDATE 2

The problem turned out to be related to the Unity container, and how EF and Unity were interacting given how I had both configured. See my answer below.

Answer

As the comments to my question note, what I was doing should work. The problem turned out to be with how my project was using the Unity dependency injection container.

For all types that were being registered, I was also configuring a virtual method interceptor. I was registering and configuring my DbContext like this:

Container.RegisterType<IInstitutionContext, InstitutionContext>();
Container.Configure<Interception>()
    .SetInterceptorFor(typeof(InstitutionContext), 
        new VirtualMethodInterceptor());

The problem was the VirtualMethodInterceptor. The interceptor was causing the InstitutionContext class to be wrapped.

Entity Framework 6 maintains a registry associating a DbContext type to its configured DbInitializer. When it sees a new type of DbContext for the first time, it looks in its internal registry to see which DbInitializer should be called. It tries to find the DbInitializer based on the DbContext's type. If it doesn't find a registered DbInitializer, it uses the default initializer, which is CreateDatabaseIfNotExists.

But when the VirtualMethodInterceptor is configured for a given DbContext type (in my case, InstitutionContext), the Unity container won't instantiate the InstitutionContext directly. Instead it wraps InstitutionContext in order to be able to intercept the virtual methods. When EF6 tries to locate the type to look up its DbInitializer, it checks the type of the DbContext, which is a wrapped type, NOT the InstitutionContext type. So it never finds a matching type, and falls back to using the default database initializer.

Removing the interceptor for the DbContext types fixed the problem.

tl;dr: Make sure your DbContext isn't wrapped by another type. For EF6's DbInitializer configuration to work, it must be checking the right DbContext type. It won't work if the DbContext is inherited from a type that has a registered DbInitializer, and it won't work if the DbContext is a wrapper type.