【问题标题】:Can't create Entity Framework code-first migrations无法创建实体框架代码优先迁移
【发布时间】:2022-01-23 19:45:01
【问题描述】:

过去几周我一直在开发 .NET Core 6 控制台应用程序(不是 ASP.NET),现在我尝试实现 Entity Framework 6 迁移到它。

但是,即使我重用了使用迁移的工作数据库模型中的一些代码,但现在我无法使其工作,并且由于缺少来自 dotnet-ef 的输出,我也一直在苦苦挣扎。

由于我不记得的原因,我重用了来自 Design-Time DbContext creation 的代码的数据库项目。我不知道这是否是我进行迁移的最佳方式,但至少它设法在以前的项目中工作。我以与之前相同的方式实现了所需的IDesignTimeDbContextFactory<DbContext> 接口:

public class MySqlContextFactory : IDesignTimeDbContextFactory<MySqlContext>
{
    public MySqlContext CreateDbContext(string[] args)
    {
        DbContextOptionsBuilder optionsBuilder = new();
        ServerVersion mariaDbVersion = new MariaDbServerVersion(new Version(10, 6, 5));
        optionsBuilder.UseMySql(DatabaseCredentials.GetConnectionString(), mariaDbVersion);

        return new MySqlContext();
    }
}

public class MySqlContext : DbContext
{
    public DbSet<Endpoint> EndpointsSet { get; set; }
    
    private readonly string _connectionString;

    public MySqlContext() : base()
        => _connectionString = DatabaseCredentials.GetConnectionString();

    public MySqlContext(string connectionString) : base()
        => _connectionString = connectionString;

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => Configurator.Configure(optionsBuilder, _connectionString);

    protected override void OnModelCreating(ModelBuilder modelBuilder)
        => Configurator.Create(modelBuilder);
}

public static void Configure(DbContextOptionsBuilder optionsBuilder, string connectionString)
{
        ServerVersion mariaDbVersion = new MariaDbServerVersion(new Version(10, 6, 5));
        optionsBuilder.UseMySql(connectionString, mariaDbVersion);
}

public static void Create(ModelBuilder modelBuilder)
{
        IEnumerable<Type> types = ReflectionUtils.GetImplementedTypes(typeof(IEntityTypeConfiguration<>));
        if (types.Any())
        {
            foreach (Type entityConfigurationType in types)
            {
                modelBuilder.ApplyConfigurationsFromAssembly(entityConfigurationType.Assembly);
            }
        }
        else
        {
            Environment.Exit((int) EExitCodes.EF_MODEL_NOT_FOUND);
        }
}

但是,当我尝试创建第一个迁移时,我收到了来自 dotnet-ef 工具的绝对非描述性输出的提示:

PS> dotnet ef migrations add Init
Build started...
Build succeeded.
PS>

但是我的项目没有进行任何迁移,也没有任何改变。所以我决定通过在 PS 命令上附加 --verbose 标志来强制 dotnet ef 告诉我更多信息:

[...]
Build succeeded.
dotnet exec --depsfile F:\pablo\Documents\source\MyBot\bin\Debug\net6.0\MyBot.deps.json --additionalprobingpath C:\Users\pablo\.nuget\packages --runtimeconfig F:\pablo\Documents\source\MyBot\bin\Debug\net6.0\MyBot.runtimeconfig.json C:\Users\pablo\.dotnet\tools\.store\dotnet-ef\6.0.1\dotnet-ef\6.0.1\tools\netcoreapp3.1\any\tools\netcoreapp2.0\any\ef.dll migrations add Init -o Migrations\Init --assembly F:\pablo\Documents\source\MyBot\bin\Debug\net6.0\MyBot.dll --project F:\pablo\Documents\source\MyBot\MyBot.csproj --startup-assembly F:\pablo\Documents\source\MyBot\bin\Debug\net6.0\MyBot.dll --startup-project F:\pablo\Documents\source\MyBot\MyBot.csproj --project-dir F:\pablo\Documents\source\MyBot\ --root-namespace MyBot--language C# --framework net6.0 --nullable --working-dir F:\pablo\Documents\source\MyBot--verbose

Using assembly 'MyBot'.
Using startup assembly 'MyBot'.
Using application base 'F:\pablo\Documents\source\MyBot\bin\Debug\net6.0'.
Using working directory 'F:\pablo\Documents\source\MyBot'.
Using root namespace 'MyBot'.
Using project directory 'F:\pablo\Documents\source\MyBot\'.
Remaining arguments: .
Finding DbContext classes...
Finding IDesignTimeDbContextFactory implementations...
Found IDesignTimeDbContextFactory implementation 'MySqlContextFactory'.
Found DbContext 'MySqlContext'.
Finding application service provider in assembly 'MyBot'...
Finding Microsoft.Extensions.Hosting service provider...
No static method 'CreateHostBuilder(string[])' was found on class 'Program'.
No application service provider was found.
Finding DbContext classes in the project...
Using DbContext factory 'MySqlContextFactory'.
PS>

我认为我可以搜索的第一件事是该工具正在搜索但不检索的 CreateHostBuilder 函数。然而,再一次,我能找到的所有文档都是指 ASP.NET 应用程序,以及我没有在我的机器人应用程序中实现的编程模式。我的应用确实通过定制的依赖注入检索服务(也许这就是 No application service provider was found. 行的原因?),但我没有找到一种方法来实现 CreateHostBuilder 函数而不改变所有内容。

仅用于添加信息,这就是我设法使用非迁移方法创建和配置 EF 模型的方式:

public static IServiceProvider GetServices(DiscordSocketClient client, CommandService commands)
    {
        ServiceCollection services = new();

        services.AddSingleton(client);
        services.AddSingleton(commands);
        services.AddSingleton<HttpClient>();

        services.AddDbContext<MySqlContext>(ServiceLifetime.Scoped);

        return AddServices(services) // builds service provider;
    }

private static async Task InitDatabaseModel(IServiceProvider provider)
    {
        MySqlContext? dbCtxt = provider.GetService<MySqlContext>();

        if (dbCtxt == null)
        {
            Environment.Exit((int) EExitCodes.DB_SERVICE_UNAVAILABLE);
        }

        await dbContext.Database.EnsureDeletedAsync();
        await dbContext.Database.EnsureCreatedAsync();
    }

但不幸的是,我的应用程序计划与数据库动态交互,因此 Code-First 配置方法对我无效。

我该如何解决这个问题?是方法问题,还是我在搞乱自定义的非 ASP.NET 依赖注入提供程序?谢谢大家

【问题讨论】:

  • 这看起来像错误:No static method 'CreateHostBuilder(string[])' was found on class 'Program'. 并且可能被欺骗:stackoverflow.com/questions/45782446/…。您可以查看您的 Program.cs 文件吗?
  • 问题是我的应用程序不是 ASP.NET,因此我没有 Startup 类可以插入到主机生成器中,也没有通常的 ASP.NET 初始化模式。我应该移植我的控制台应用程序以实现这一目标吗?

标签: c# entity-framework .net-core entity-framework-migrations .net-6.0


【解决方案1】:

您的IDesignTimeDbContextFactory 存在问题。 EF Core 正在尝试为您的这个工厂创建一个MySqlContext

public class MySqlContextFactory : IDesignTimeDbContextFactory<MySqlContext>
{
    public MySqlContext CreateDbContext(string[] args)
    {
        // set up options
        DbContextOptionsBuilder optionsBuilder = new();
        ServerVersion mariaDbVersion = new MariaDbServerVersion(new Version(10, 6, 5));
        optionsBuilder.UseMySql(DatabaseCredentials.GetConnectionString(), mariaDbVersion);
        
        // *** this is the issue *** 
        // return default constructor W/O options (ie, UseMySql is never called)
        return new MySqlContext(); 
    }
}

您可以将此构造函数添加到您的 DbContext 类中:

public MySqlContext(DbContextOptions<MySqlContext> options)
        : base(options)
    {
    }

然后return new MySqlContext(optionsBuilder.Options) 来自您的工厂。

【讨论】:

  • 谢谢!就是这样。我仍然需要对所有内容进行一些重构以遵循 DI 原则,但这确实起到了作用。顺便说一句,问题出在DatabaseCredentials.GetConnectionString() 方法中,该方法检索了在调用dotnet ef migrations add 时不存在的环境变量中的ConnectionString 所需的参数。不幸的是,即使使用--verbose 标志,在尝试检索环境变量时也没有抛出异常的输出。
猜你喜欢
  • 2013-12-29
  • 1970-01-01
  • 1970-01-01
  • 2017-10-31
  • 2013-01-02
  • 1970-01-01
相关资源
最近更新 更多