使用 Entity Framework Core 时,通过代码自动 Migration

一 介绍

  在使用 Entity Framework Core (下面就叫 EF Core 吧)进行开发时,如果模型有变动,我们要在用 EF Core 提供的命令行工具进行手工迁移,然后再运行程序。但是为了效率,我想能不能在程序的入口处进行 Migration 呢?从个人经验来说应该是可以,因为 EF Tool 虽然提供了 CLI 但是它最终也是被程序解析这些命令。下面就开始分析,如何通过代码进行 Migration 。

二 分析

  首先我们要先了解,在使用 EF Core 的 CLI 时,要执行两个步骤:

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

    第一步:生成 Migration 文件;

    第二步:更新变更项到数据库;

  既然是先生成 Migration 文件再更新,那么在 EF Core 里面一定有对应的模块做这件事情。下面我们看一下 EF Core 项目的结构。从中我们确实找到关于 Migration 的模块。在 Migrations/Design 目录的类名称上我们可以看出来,它就是生成 Migration 文件的。这里先到这儿。

  使用 Entity Framework Core 时,通过代码自动 Migration,使用 Entity Framework Core 时,通过代码自动 Migration 随笔 第1张

  找到了生成 Migration 文件的入口,我们再来找一下如何通过代码将这些变更更新到数据库中。

  在使用 EF Core 的时候,我们都要通过继承 DbContext 来编写自己的 DbContext 子类。在 DbContext 类中我们找到了一个 Database 属性。如下图所示:

  使用 Entity Framework Core 时,通过代码自动 Migration,使用 Entity Framework Core 时,通过代码自动 Migration 随笔 第2张

  然后查看了 DatabaseFacde 这个类,并没有发现执行迁移相关的函数。通过代码搜索,我在 RelationalDatabaseFacadeExtensions 这个类中有一个 Migration() 扩展方法。通过注释的解析,我也确定了它就是执行 Migration 文件,并将变更更新到数据库。

  这两个步骤对应的代码我们都找到了,下面我们就编写一段儿代码,完成自动将模型变更更新到数据库的功能。

 1 public class AutoMigration
 2 {
 3     private readonly IServiceProvider _serviceProvider;
 4     private InformationDbContext _context;
 5 
 6     public AutoMigration(IServiceProvider serviceProvider)
 7     {
 8         _serviceProvider = serviceProvider;
 9         _context = serviceProvider.GetService<InformationDbContext>();
10     }
11 
12     public void Migrator()
13     {
14         var path = Path.Combine(AppContext.BaseDirectory, "..\\..\\..\\Migrations\\");
15         if (!Directory.Exists(path))
16         {
17             Directory.CreateDirectory(path);
18         }
19         else
20         {
21             Directory.GetFiles(path).ToList().ForEach(File.Delete);
22         }
23 
24         using (_context)
25         {
26             var services = ((IInfrastructure<IServiceProvider>) _context).Instance;
27             var codeHelper = new CSharpHelper();
28             var scaffolder = ActivatorUtilities.CreateInstance<MigrationsScaffolder>(services,
29                 new CSharpMigrationsGenerator(codeHelper, new CSharpMigrationOperationGenerator(codeHelper),
30                     new CSharpSnapshotGenerator(codeHelper)));
31             
32             var projectDir = Path.Combine(path, "..\\");
33             var migrationAssembly = new MigrationsAssembly(new CurrentDbContext(_context), _context.Options, new MigrationsIdGenerator());
34             scaffolder.GetType().GetField("_migrationsAssembly", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(scaffolder, migrationAssembly);
35 
36             var readonlyDic = new ReadOnlyDictionary<string,TypeInfo>(new Dictionary<string, TypeInfo>());
37             migrationAssembly.GetType().GetField("_migrations", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(migrationAssembly, new LazyRef<IReadOnlyDictionary<string, TypeInfo>>(readonlyDic));
38             var migration = scaffolder.ScaffoldMigration("Information.Migrations", "Information");
39 
40             scaffolder.Save(projectDir, migration, path);
41 
42             //另外一种保存方式
43             //File.WriteAllText($"Migrations\\{migration.MigrationId}{migration.FileExtension}", migration.MigrationCode);
44             //File.WriteAllText("Migrations\\" +
45             //    migration.MigrationId + ".Designer" + migration.FileExtension,
46             //    migration.MetadataCode);
47             //File.WriteAllText("Migrations\\" + migration.SnapshotName + migration.FileExtension,
48             //    migration.SnapshotCode);
49         }
50 
51         using(_context = (InformationDbContext)_serviceProvider.GetService<IDbContext>())
52         {
53             _context.Database.Migrate();
54         }
55     }
56 }

  另外一个注意点:我们需要指定一下迁移文件所在项目。

1 services.AddDbContext<InformationDbContext>(opt =>
2 {
3     var connectionString = configuration["ConnectionStrings:DefaultConnection"];
4     opt.UseSqlServer(connectionString, optionBuilder =>
5     {
6         optionBuilder.MigrationsAssembly("Information");
7     });
8 });

 

三 总结

  通过上面的分析可以知道,其实我们就是把 CLI 的两个命令通过代码实现了一下。在 Startup 文件中进行调用即可。

原文链接:https://www.cnblogs.com/jRoger/p/entity-framework-core-auto-migration.html

 

,

一 介绍

  在使用 Entity Framework Core (下面就叫 EF Core 吧)进行开发时,如果模型有变动,我们要在用 EF Core 提供的命令行工具进行手工迁移,然后再运行程序。但是为了效率,我想能不能在程序的入口处进行 Migration 呢?从个人经验来说应该是可以,因为 EF Tool 虽然提供了 CLI 但是它最终也是被程序解析这些命令。下面就开始分析,如何通过代码进行 Migration 。

二 分析

  首先我们要先了解,在使用 EF Core 的 CLI 时,要执行两个步骤:

    第一步:生成 Migration 文件;

    第二步:更新变更项到数据库;

  既然是先生成 Migration 文件再更新,那么在 EF Core 里面一定有对应的模块做这件事情。下面我们看一下 EF Core 项目的结构。从中我们确实找到关于 Migration 的模块。在 Migrations/Design 目录的类名称上我们可以看出来,它就是生成 Migration 文件的。这里先到这儿。

  使用 Entity Framework Core 时,通过代码自动 Migration,使用 Entity Framework Core 时,通过代码自动 Migration 随笔 第3张

  找到了生成 Migration 文件的入口,我们再来找一下如何通过代码将这些变更更新到数据库中。

  在使用 EF Core 的时候,我们都要通过继承 DbContext 来编写自己的 DbContext 子类。在 DbContext 类中我们找到了一个 Database 属性。如下图所示:

  使用 Entity Framework Core 时,通过代码自动 Migration,使用 Entity Framework Core 时,通过代码自动 Migration 随笔 第4张

  然后查看了 DatabaseFacde 这个类,并没有发现执行迁移相关的函数。通过代码搜索,我在 RelationalDatabaseFacadeExtensions 这个类中有一个 Migration() 扩展方法。通过注释的解析,我也确定了它就是执行 Migration 文件,并将变更更新到数据库。

  这两个步骤对应的代码我们都找到了,下面我们就编写一段儿代码,完成自动将模型变更更新到数据库的功能。

 1 public class AutoMigration
 2 {
 3     private readonly IServiceProvider _serviceProvider;
 4     private InformationDbContext _context;
 5 
 6     public AutoMigration(IServiceProvider serviceProvider)
 7     {
 8         _serviceProvider = serviceProvider;
 9         _context = serviceProvider.GetService<InformationDbContext>();
10     }
11 
12     public void Migrator()
13     {
14         var path = Path.Combine(AppContext.BaseDirectory, "..\\..\\..\\Migrations\\");
15         if (!Directory.Exists(path))
16         {
17             Directory.CreateDirectory(path);
18         }
19         else
20         {
21             Directory.GetFiles(path).ToList().ForEach(File.Delete);
22         }
23 
24         using (_context)
25         {
26             var services = ((IInfrastructure<IServiceProvider>) _context).Instance;
27             var codeHelper = new CSharpHelper();
28             var scaffolder = ActivatorUtilities.CreateInstance<MigrationsScaffolder>(services,
29                 new CSharpMigrationsGenerator(codeHelper, new CSharpMigrationOperationGenerator(codeHelper),
30                     new CSharpSnapshotGenerator(codeHelper)));
31             
32             var projectDir = Path.Combine(path, "..\\");
33             var migrationAssembly = new MigrationsAssembly(new CurrentDbContext(_context), _context.Options, new MigrationsIdGenerator());
34             scaffolder.GetType().GetField("_migrationsAssembly", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(scaffolder, migrationAssembly);
35 
36             var readonlyDic = new ReadOnlyDictionary<string,TypeInfo>(new Dictionary<string, TypeInfo>());
37             migrationAssembly.GetType().GetField("_migrations", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(migrationAssembly, new LazyRef<IReadOnlyDictionary<string, TypeInfo>>(readonlyDic));
38             var migration = scaffolder.ScaffoldMigration("Information.Migrations", "Information");
39 
40             scaffolder.Save(projectDir, migration, path);
41 
42             //另外一种保存方式
43             //File.WriteAllText($"Migrations\\{migration.MigrationId}{migration.FileExtension}", migration.MigrationCode);
44             //File.WriteAllText("Migrations\\" +
45             //    migration.MigrationId + ".Designer" + migration.FileExtension,
46             //    migration.MetadataCode);
47             //File.WriteAllText("Migrations\\" + migration.SnapshotName + migration.FileExtension,
48             //    migration.SnapshotCode);
49         }
50 
51         using(_context = (InformationDbContext)_serviceProvider.GetService<IDbContext>())
52         {
53             _context.Database.Migrate();
54         }
55     }
56 }

  另外一个注意点:我们需要指定一下迁移文件所在项目。

1 services.AddDbContext<InformationDbContext>(opt =>
2 {
3     var connectionString = configuration["ConnectionStrings:DefaultConnection"];
4     opt.UseSqlServer(connectionString, optionBuilder =>
5     {
6         optionBuilder.MigrationsAssembly("Information");
7     });
8 });

 

三 总结

  通过上面的分析可以知道,其实我们就是把 CLI 的两个命令通过代码实现了一下。在 Startup 文件中进行调用即可。

原文链接:https://www.cnblogs.com/jRoger/p/entity-framework-core-auto-migration.html

 

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄