EntityFramework Core自动迁移

58

在ASP.NET Core项目中,是否有代码可以执行Entity Framework Core Code First的自动迁移?

我在MVC4/5中只需添加以下代码即可完成此操作

Database.SetInitializer(new MigrateDatabaseToLatestVersion<AppDbContext, MyProject.Migrations.Configuration>());
public Configuration() {
          AutomaticMigrationsEnabled = true;
        }

当实体被更改时,这将节省时间。


它被称为EntityFramework Core 1.0,谢谢。 - Tseng
10个回答

56
你可以在 Startup.cs 中调用 context.Database.Migrate()
例如:
using (var context = new MyContext(...))
{
    context.Database.Migrate();
}

2
我的上下文看起来是这样的,我不知道在选项中要放什么:public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } - Lapenkov Vladimir
请参考这篇文章,了解如何手动配置您的上下文:https://dev59.com/3loT5IYBdhLWcg3wlgPn 和 https://github.com/aspnet/EntityFramework/issues/6493。 - Frank Odoom
1
我在 Startup.cs 中按照以下方式进行操作:在定义路由之后,我添加了以下内容:var options = new DbContextOptionsBuilder<ApplicationDbContext>(); options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));使用 ApplicationDbContext(options.Options) 创建上下文对象,然后执行迁移操作:using (var context = new ApplicationDbContext(options.Options)) { context.Database.Migrate(); }但是这种方法并没有执行迁移。 - Lapenkov Vladimir
将您的上下文注册到依赖注入中 https://docs.efproject.net/en/latest/platforms/aspnetcore/new-db.html - Frank Odoom
17
回答是错误的,除非添加了每个迁移步骤,否则迁移不起作用,并且它不等同于 AutomaticMigrationsEnabled - Akash Kava
显示剩余3条评论

40

EF Core不支持自动迁移,因此您需要手动进行迁移。

从自动迁移功能的角度来看,我们没有计划在EF Core中实现它,因为经验表明基于代码的迁移方法是一种更可管理的方法。

您可以在这里阅读完整的故事:不要实现自动迁移


7
无论如何,在我参与的所有项目中,这种方法对我都非常有效,尤其是在项目建设阶段,维护工作非常少。很遗憾这种方法已经消失了,但我可以想象有时候它的额外开销可能不值得。 - Arwin
2017年9月,EF Core 2.0仍然没有支持自动迁移的机会吗? - Afshar Mohebi
也许这可以帮助:@Afshar https://github.com/aspnet/EntityFrameworkCore/issues/8045 - Sampath
哦,好的。实际上我现在没有使用这个技术栈,因为我现在只在Ionic移动应用程序上工作。但根据我的帖子链接,他们将其标记为“closed-wont-fix”@Afshar。 - Sampath
这里是手动创建迁移并在服务器上自动运行的完整说明链接:https://blog.rsuter.com/automatically-migrate-your-entity-framework-core-managed-database-on-asp-net-core-application-start/ - Ali Dehghan Tarzeh
显示剩余3条评论

27

这是IdentityServer4的实现方式http://identityserver.io

public void ConfigureServices(IServiceCollection services)
{
    var connectionString = Configuration.GetConnectionString("DefaultConnection");
    var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;

    // Add framework services.
    services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(connectionString));
    ...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    // this will do the initial DB population
    InitializeDatabase(app);
}

private void InitializeDatabase(IApplicationBuilder app)
{
    using (var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
    {
        scope.ServiceProvider.GetRequiredService<ApplicationDbContext>().Database.Migrate();
        scope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();
        ...
    }
}

2
他们不再这样做了。我已经检查过他们的源代码。 - Rosdi Kasim
这种技术对我很有效。谢谢! - Johnny Oshika

20

EF Core不支持自动迁移。必须手动进行迁移。如果要自动应用所有现有的手工迁移,则需要在Program类中添加以下代码:

public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateWebHostBuilder(args).Build();

        using (var scope = host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;

            try
            {
                var context = services.GetRequiredService<MyDbContext>();
                context.Database.Migrate(); // apply all migrations
                SeedData.Initialize(services); // Insert default data
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred seeding the DB.");
            }
        }

        host.Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

1
你只需要访问索引为0的元素,为什么要使用数组? - ProfK
6
为了在不打开包装的情况下,拥有一个用于锁定和快速检查的字段。 - Mentor
1
这是@Matt-Morgan在上面回答的重复内容。尽管您原始答案的时间戳为2017-02-17,但您在2018-06-07添加了作用域依赖注入的dbcontext解决方案,而Matt在2017-04-20发布了相同的答案。 - Daniel

6

在数据库上下文构造函数中调用Database.Migrate()


对我有用 :) - Naveen Kumar V
所有的例子都是针对ASP .NET的。但最终,这种方法适用于C# 5和EFCore。我花了几个小时才弄清楚... - Jucko Thirteen

6

根据微软文档,

https://learn.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro

如果您使用依赖注入,首先需要设置一个静态类Data/DbInitializer.cs并添加以下代码:

public static class DbInitializer
{
    public static void Initialize(ApplicationDbContext context)
    {
        context.Database.Migrate();

        // Add Seed Data...
    }
}

请注意,这也是您可以添加种子数据的地方。

接下来,在您的Program.cs文件中添加以下代码:

public static void Main(string[] args)
    {
        var host = BuildWebHost(args);

        using (var scope = host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;
            try
            {
                var environment = services.GetRequiredService<IHostingEnvironment>();

                if (!environment.IsDevelopment())
                {
                    var context = services.GetRequiredService<ApplicationDbContext>();
                    DbInitializer.Initialize(context);
                }
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred while seeding the database.");
            }
        }

        host.Run();
    }

在我的情况下,我正在检查环境,以确保我处于开发状态,这样我就可以控制迁移/更新。然而,在生产中,我希望它们自动执行以实现持续集成。正如其他人所提到的,这可能不是最佳实践,但对于小型项目来说,效果非常好。

5

我正在使用Asp Net Core 2.0.7的自动迁移代码。

    // startup.cs
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        // configure app

        SeedData.Initialize(app.ApplicationServices);
    }       

    // dbInitializer.cs
    public static class SeedData
    {
        public static void Initialize(IServiceProvider serviceProvider)
        {
            using (var serviceScope = serviceProvider.CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetService<ApplicationDbContext>();

                // auto migration
                context.Database.Migrate();

                // Seed the database.
                InitializeUserAndRoles(context);
            }
        }

        private static void InitializeUserAndRoles(ApplicationDbContext context)
        {
            // init user and roles  
        }
    }

2
如果模型变化很大,并且您管理的是中大型团队,则迁移在开发阶段至少会带来更多问题而不是解决方案。我发布了一个自动迁移的NuGet包,用于.NET Core,EFCore.AutomaticMigrations - https://www.nuget.org/packages/EFCore.AutomaticMigrations/,因此不再需要手动迁移。
您可以直接在Program类中调用,如下所示:
 public static void Main(string[] args)
    {
        var host = CreateWebHostBuilder(args);

        using (var scope = host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;
            var loggerFactory = services.GetRequiredService<ILoggerFactory>();
            var logger = loggerFactory.CreateLogger<Program>();
            try
            {
                var environment = services.GetRequiredService<IWebHostEnvironment>();

                if (environment.IsDevelopment())
                {
                    var context = services.GetRequiredService<ApplicationContext>();
                    MigrateDatabaseToLatestVersion.ExecuteAsync(context).Wait();
                }
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "An error occurred creating/updating the DB.");
            }
        }

        host.Run();
    }

这个能和Postgres一起工作还是只能和SQL Server一起工作? - kaptcha
此软件包仅适用于 Sql Server。 - Ionut N

1

Frank Odoom的回答在4年后的.net 5仍然有效,但在运行时调用迁移不是预期的上下文...而且,似乎从来没有,因为它要求我们使用DbContextOptions模拟DbContext,其文档明确指出:

"DbContext要使用的选项。通常您会覆盖OnConfiguring(DbContextOptionsBuilder)或使用DbContextOptionsBuilder创建此类的实例,并且它不是设计用于直接构建应用程序代码。"

这是我的建议:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
       // database provider is configured before runtime migration update is applied e.g:
       optionsBuilder.UseSqlServer(ConnectionString);
       Database.Migrate();
}

编辑:如果您在同一项目中使用多个DBContexts,我的建议实际上是可怕的...它将多次迁移数据库。这很可能不会破坏任何东西,但它会显著减慢启动速度。


在 OnConfiguring 中尝试调用 Database.Migrate() 时,我遇到了这个错误:"System.InvalidOperationException: 'An attempt was made to use the context instance while it is being configured. A DbContext instance cannot be used inside 'OnConfiguring' since it is still being configured at this point. This can happen if a second operation is started on this context instance before a previous operation completed. Any instance members are not guaranteed to be thread safe." - mrNone
在尝试在OnConfiguring中调用Database.Migrate()时,我遇到了这个错误:"System.InvalidOperationException: '在配置上下文实例时尝试使用上下文实例。由于在此时仍在配置上下文实例,因此不能在'OnConfiguring'中使用DbContext实例。如果在上一个操作完成之前在此上下文实例上启动了第二个操作,则可能会发生此情况。任何实例成员都不能保证是线程安全的。" - undefined
这很有道理。我的用例非常特定于单个DBContext。对于新版本的.net来说,实现一个故障转移机制,在存在多个上下文的情况下不允许它是明智的。如果你仍在寻找一种方法来做到这一点:这就是我在.net6中使用的方法 https://jasonwatmore.com/post/2022/02/01/net-6-execute-ef-database-migrations-from-code-on-startup而且更加优雅。 - Mikie Sterner
这是有道理的。我的使用情况非常特定于单个DBContext。对于较新版本的.NET来说,实施一个故障安全机制是明智的,以防止在存在多个上下文的情况下允许这种情况发生。如果你仍然在寻找一种方法来实现它:这是对我来说在.NET 6上有效的方法 https://jasonwatmore.com/post/2022/02/01/net-6-execute-ef-database-migrations-from-code-on-startup而且更加优雅。 - undefined

0

我最好的建议是不要使用自动迁移。最好手动添加迁移,避免批量迁移,并坚持使用手动迁移的最佳实践。

自动迁移不是一个魔法工具,在许多情况下,您可能需要添加一些附加更改来进行迁移。只有通过手动迁移才能完成。

要启用迁移,请在程序包管理器控制台中键入“enable-migrations”。

这样,您将完全掌控升级或降级数据库,还可以轻松地跟踪迁移。

在程序包管理器控制台中只需要三个简单的步骤。

1)add-migrations [为您的迁移命名]

2)生成了迁移文件,您可以对其进行审查并进行更改

3)现在您的迁移已完成,可以执行update-database命令。

手动迁移的处理方式比较简单!


5
当模型频繁变更且多人共同开发一个项目时,迁移会带来更多问题而非解决方案。此外,拥有数千个迁移会使构建变得更加缓慢和复杂,没有任何意义。在这种情况下,自动迁移似乎是更为合理的方法。 - eka808

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接