【问题标题】:Can I inject dependency into migration (using EF-Core code-first migrations)?我可以将依赖项注入迁移(使用 EF-Core 代码优先迁移)吗?
【发布时间】:2017-07-27 10:08:18
【问题描述】:

我尝试将IConfiguration 注入到迁移中(在构造函数中),并得到异常:“没有为此对象定义无参数构造函数。”

有什么解决方法吗?

【问题讨论】:

  • 您能否详细描述一下您通过注入服务想要完成的工作?
  • @bricelam,尝试编写将数据插入数据库的迁移,此数据取决于其他数据库中的数据(未映射到实体框架)。所以迁移应该从配置中获得第二个数据库连接字符串(因为这对于 dev/staging/prod envs 不同)。我们最终在所有环境中手动运行脚本。

标签: entity-framework dependency-injection asp.net-core entity-framework-core entity-framework-migrations


【解决方案1】:

您不能,迁移需要能够在您的应用程序上下文之外运行。

由于 Entity-framework 命令行工具会分析您的代码,但不会运行 startup.cs 类。

也不建议这样做。你的迁移应该很简单,不依赖任何东西。如果是这样,它可能会导致主要的运行时副作用,其中缺少配置可能会导致生产中的表或列丢失。

补充建议

如果涉及大量小/相等/手动更改。最好的方法是生成您的迁移文件。为什么?这样,您的迁移将是确定性的:您知道结果会是什么。如果您的迁移中的某条线路失败了,那么它的原因就很简单明了,而且很容易(呃)修复。

【讨论】:

  • 不,它运行代码。您可以将throw Exception("Message"); 插入到您的配置中,然后执行添加迁移,您将在控制台中看到“消息”。但是会创建迁移。
  • “迁移需要能够在应用程序的上下文之外运行”。错误(至少在当前版本的 EF 中)。 EF 工具使用您的程序主机构建器来发现创建和执行迁移所需的所有服务和配置。
  • @jeremyLakeman 感谢更新。它是否也在启动时使用与应用程序相同的环境变量和配置运行?
  • 环境变量是...来自环境。 AFAIK,主机已构建,但未启动。其中应该包括配置。然后使用服务提供者发现所需的 EF 服务。
  • 好的,感谢您的更新。我不再真正在 dotnet 上工作,所以这就是我问的原因。您可能会建议更改我的答案:)
【解决方案2】:

有一种方法可以做你想做的事。在我的场景中,我想通过 DbContext 使用连接字符串中的数据库名称。使用 EF 核心 2.1.1。 The code is modified from here

创建自定义 MigrationsAssembly 服务

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Internal;
using System;
using System.Reflection;

public class ContextAwareMigrationsAssembly : MigrationsAssembly
{
    private readonly DbContext context;

    public ContextAwareMigrationsAssembly(
        ICurrentDbContext currentContext,
        IDbContextOptions options,
        IMigrationsIdGenerator idGenerator,
        IDiagnosticsLogger<DbLoggerCategory.Migrations> logger) : base(currentContext, options, idGenerator, logger)
    {
        context = currentContext.Context;
    }

    /// <summary>
    /// Modified from http://weblogs.thinktecture.com/pawel/2018/06/entity-framework-core-changing-db-migration-schema-at-runtime.html
    /// </summary>
    /// <param name="migrationClass"></param>
    /// <param name="activeProvider"></param>
    /// <returns></returns>
    public override Migration CreateMigration(TypeInfo migrationClass, string activeProvider)
    {
        var hasCtorWithDbContext = migrationClass
                .GetConstructor(new[] { typeof(DbContext) }) != null;

        if (hasCtorWithDbContext)
        {
              var instance = (Migration)Activator.CreateInstance(migrationClass.AsType(), context);
              instance.ActiveProvider = activeProvider;
              return instance;
        }

        return base.CreateMigration(migrationClass, activeProvider);
    }
}

用您的自定义类替换 DbContext 中的 IMigrationAssembly 服务

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.ReplaceService<IMigrationsAssembly, ContextAwareMigrationsAssembly>();
}

然后您可以在迁移中添加DbContext 参数。

public Migration20180801(DbContext context)
{
    DatabaseName = context.Database.GetDbConnection().Database;
}

在您的情况下,您可以将所有 DbContext 引用替换为 IConfiguration 以及 CreateMigration 覆盖中的相关实例。

【讨论】:

  • 如果使用 DbContext pooling,如果使用 OnConfiguring 修改 DbContextOptions 会抛出异常。您可以使用 dbContextOptionsBuilder.ReplaceService() 代替,假设您没有更改 EF 内部服务提供者。
  • 有人真的用 IConfiguration 试过这个吗?我尝试将 IConfiguration 注入到 ContextAwareMigrationsAssembly 之类的类中,但没有解决。
【解决方案3】:

如果它只是关于您的连接字符串(是吗?),您可能需要检查 this answer,它基本上在您的启动项目(而不是在您的迁移项目中)建议此代码:

var myConnectionString = Configuration.GetConnectionString(myConnectionStringName);
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(
    myConnectionString ,
    x => x.MigrationsAssembly(myDbContextAssemblyName)));

【讨论】:

    猜你喜欢
    • 2016-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-18
    • 1970-01-01
    • 2012-09-15
    • 2013-11-05
    • 1970-01-01
    相关资源
    最近更新 更多