2

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.

Community
  • 1
  • 1
Katie Kilian
  • 6,815
  • 5
  • 41
  • 64
  • Have you tried the `disableDatabaseInitialization` attribute? https://msdn.microsoft.com/en-us/data/jj556606#Initializers – jmoerdyk Sep 08 '16 at 21:23
  • I've tried that, but I am not sure I've put it in the right place. I've put it in the Web.config of the web project, but this loads through Azure, so I'm not sure if that changes something. Is there any way to do that through code instead of a config file? – Katie Kilian Sep 08 '16 at 22:05
  • That should work depending on how the projects are structured. Here is an alternative method: https://romiller.com/2014/06/10/reducing-code-first-database-chatter/ – Steve Greene Sep 09 '16 at 14:02

1 Answers1

0

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.

Katie Kilian
  • 6,815
  • 5
  • 41
  • 64