EntityFrameworkCore+CodeFirst:根据实体自动生成数据库(二) 程序包管理控制台迁移
二、程序包管理控制台迁移
需要在DAL层引用包:Microsoft.EntityFrameworkCore.Tools
然后在控制台程序中也需要引用包:Microsoft.EntityFrameworkCore.Design
这样,就完成了需要的组件的引用了。
接下来打开程序包管理控制台,在vs中的“工具”中
将默认项目切换为CodeFirst.DAL
接下来敲入命令:
Add-Migration MyCodeFirst
MyCodeFirst:为自定义,每次构建都不能重名。比如下次构建,可以是MyCodeFirst1
如下:
命令会显示构建成功,这时候在我们的DAL层中会多了一个Migrations目录,每次的迁移文件都会存在该目录中
20220830065221_MyCodeFirst.cs :是针对本次迁移生成的文件。每次迁移都会生成一个对应的迁移文件。里面有一个Up方法和一个Down方法,Up方法表示要执行的操作,Down方法相当于回滚,如果需要还原到上一个状态,就会执行Down方法。
MyDbContextModelSnapshot.cs :生成的配置文件。只会生成一次。
接下来就是执行这些文件来创建数据库,执行以下命令:
update-database MyCodeFirst //指向上面自定义的名称,意思是执行那个文件
这样就生成了数据库结构了
比如下一次结构有修改,那就执行
Add-Migration 新定义的名称
再执行 update-database 新定义的名称
即可。
在数据库中,我们会发现多了一个__EFMigrationsHistory,该表是自动生成的,记录迁移过程
该种方式与第一种方式好在于,对于数据结构的变化,可以每次都更新到数据库。
但在生产环境下,不太适用,我们无法在服务器上通过 VS的nuget控制台去执行update-datebase 命令。
=======================================
SQL脚本
除了用 Add-Migration 命令和update-database 结合之外,我们还可以将变更的结构生成sql进行核对,使用script-migration命令即可
script-migration:不指定版本,默认生成最新迁移版本的(全部)SQL脚本
script-migration source target :source 是源版本,target 是目标版本,根据两个版本的结构差异而生成sql
首先,我们先使用 Add-Migration test1命令,生成迁移代码,然后再使用 script-migration 命令生成脚本
执行后,再vs中会弹出一个.sql的文件,如
我们可以人工核对脚本的正确性,再拿到远程服务器上去执行。
但有时候,我们是在本地多次改了结构,会多次进行本地数据结构迁移。这时候,更新到远程时,需要将本地新增加的sql文件按照生成的时间顺序,分别放到远程上去执行,不然结构就会不对。
幂等 SQL 脚本
上面生成的 SQL 脚本只能用于将架构从一个迁移更改为另一个迁移;你需要适当地应用脚本,并且仅应用于处于正确迁移状态的数据库。 EF Core 还支持生成幂等脚本,此类脚本将在内部检查已经应用哪些迁移(通过迁移历史记录表),并且只应用缺少的迁移。 如果不确知应用到数据库的最后一个迁移,或者需要部署到多个可能分别处于不同迁移的数据库,此类脚本非常有用。
使用的命令是:Script-Migration -Idempotent
也可以使用dotnet命令
dotnet ef migrations script --idempotent
====================================================
假如我们已经了Add-Migration MyCodeFirst,但是在生产环境下,我们既然没办法在nuget上执行 update-datebase 命令,那么我们也可以用应用程序来执行
原理都是调用dbcontext上下文对象的.Database.Migrate() 方法来实现。
可以有两种使用方式,不同应用场景:
1:直接在startup.cs的Configure方法中直接调用,如
var dbContext = app.ApplicationServices.GetRequiredService<MyDbContext>(); dbContext.Database.Migrate();
这样也可以实现更新。
2:写扩展类来执行
代码放这里,看着感觉不太顺眼,不太合理对吧。
那就定义一个扩展类DatabaseManagementService
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using CodeFirst; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace WebApplication1 { public static class DatabaseManagementService { // Getting the scope of our database context public static IApplicationBuilder UseMigrationInitialisation( this IApplicationBuilder app, IWebHostEnvironment env, IConfiguration configuration) { using (var serviceScope = app.ApplicationServices.CreateScope()) { using (var context = serviceScope.ServiceProvider.GetService<MyDbContext>()) { var db = context.Database; db.Migrate(); SeedData(context, env, configuration); //创建数据种子 } } return app; } private static void SeedData( MyDbContext _context, IWebHostEnvironment env, IConfiguration configuration ) { if (env.IsDevelopment()) { //Do something for DEV } else { //Do something for Production } if(!_context.Set<Student>().Any()) //若是空,则写入默认数据,避免重复写入 { Student model = new Student(); model.Name = "风扇"; model.sex = 0; model.Age = 10; _context.Set<Student>().Add(model); } _context.SaveChanges(); } } }
然后在 startup.cs的路由终结点时,调用
app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }) .UseMigrationInitialisation(env, Configuration);
通过这种方法,也可以达到目的,同时又能初始化一些数据。
但是,在程序端执行,有几个点需要特别注意:
- 如果应用程序的多个实例正在运行,这两个应用程序可能会尝试同时应用迁移并失败(更糟糕的情况是导致数据损坏)。
- 同样,如果一个应用程序正在访问数据库,而另一个应用程序正在迁移它,这可能会导致严重的问题。
- 应用程序必须具有提升的访问权限才能修改数据库架构。 在生产环境中限制应用程序的数据库权限通常是一种很好的做法。
- 出现问题时,能够回滚已应用的迁移很重要。 其他策略可以轻松提供此功能,并且开箱即用。
- 程序会直接应用 SQL 命令,不给开发人员检查或修改的机会。 这在生产环境中可能会很危险。
也就是说,这个程序建议应该是独立的,专门给数据库迁移使用,而不是在业务程序中执行。
至此,程序包管理控制台迁移完成。
更多分享,请大家关注我的个人公众号: