在使用 Unity 配置依赖项注入时,如何防止 EF Code First 尝试 运行 迁移我的数据库?

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

如何防止 CodeFirst 尝试 运行 迁移我的数据库?

我正在 SqlException 抛出以下错误消息:

Invalid object name 'dbo.__MigrationHistory'.

我的日志框架正在捕获并记录这些异常。我需要防止异常发生,因此我的应用程序不会生成大量虚假日志消息。

我了解 __MigrationHistory table 是代码优先迁移的一部分。我确实使用代码优先,但我使用它的迁移。 (该项目使用FluentMigrator instead.) According to this blog post and this answer,要禁用该错误,我需要执行以下操作:

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

(这当然是针对名为 InstitutionContextDbContext 对象。)

我已经这样做了,并验证了代码行是否被命中。但是,我仍然在日志中收到错误。

This answer解释了是怎么回事,我认为基本上是正确的。简而言之,EF 正在探测 __MigrationsHistory table,在找不到它时抛出异常,然后继续而不对迁移做任何事情。但是答案并没有解释如何抑制异常的发生。

如何防止 EF Code First 迁移尝试 运行 并抛出其 SqlException

更新 1

我尝试通过 App.config/Web.config 文件配置设置,但也没有用。我还尝试将它从我的 DbContext 上的静态方法移动到它自己的配置 class,如下所示:

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

class 与 DbContext 在同一个程序集中,就像它应该的那样。这也没有用。

更新 2

问题原来与 Unity 容器有关,以及 EF 和 Unity 是如何交互的(鉴于我的配置方式)。请参阅下面的

正如对我的问题注释的评论,我所做的应该有效。问题出在我的项目如何使用 Unity 依赖注入容器。

对于所有正在注册的类型,我还配置了一个虚拟方法拦截器。我是这样注册和配置我的 DbContext 的:

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

问题出在 VirtualMethodInterceptor。拦截器导致 InstitutionContext class 被包装。

Entity Framework 6 维护一个注册表,将 DbContext 类型与其配置的 DbInitializer 相关联。当它第一次看到新类型的 DbContext 时,它会在其内部注册表中查找应该调用哪个 DbInitializer。它试图根据 DbContext 的类型找到 DbInitializer。如果它没有找到已注册的 DbInitializer,它会使用默认的初始化程序,即 CreateDatabaseIfNotExists.

但是当 VirtualMethodInterceptor 被配置为给定的 DbContext 类型(在我的例子中,InstitutionContext),Unity 容器不会直接实例化 InstitutionContext .相反,它包装 InstitutionContext 以便能够拦截虚拟方法。当 EF6 尝试定位类型以查找其 DbInitializer 时,它会检查 DbContext 的类型,这是一个包装类型,NOT InstitutionContext 类型。所以它永远找不到匹配的类型,并回退到使用默认的数据库初始化程序。

删除 DbContext 类型的拦截器解决了这个问题。

tl;dr: 确保您的 DbContext 没有被其他类型包裹。要使 EF6 的 DbInitializer 配置正常工作,它必须检查正确的 DbContext 类型。如果 DbContext 是从已注册 DbInitializer 的类型继承的,它将不起作用,如果 DbContext 是包装类型,它将不起作用。