使用 Entity Framework Core 時,通過代碼自動 Migration


一 介紹

  在使用 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 文件的。這里先到這兒。

  

  找到了生成 Migration 文件的入口,我們再來找一下如何通過代碼將這些變更更新到數據庫中。

  在使用 EF Core 的時候,我們都要通過繼承 DbContext 來編寫自己的 DbContext 子類。在 DbContext 類中我們找到了一個 Database 屬性。如下圖所示:

  

  然后查看了 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 文件中進行調用即可。為什么想這么干?因為在實際開發的時候,來回切換窗口心里覺得不爽了唄。:)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM