一 介紹
在使用 Entity Framework Core (下面就叫 EF Core 吧)進行開發時,若是模型有變更,咱們要在用 EF Core 提供的命令行工具進行手工遷移,而後再運行程序。可是爲了效率,我想能不能在程序的入口處進行 Migration 呢?從我的經驗來講應該是能夠,由於 EF Tool 雖然提供了 CLI 可是它最終也是被程序解析這些命令。下面就開始分析,如何經過代碼進行 Migration 。html
二 分析
首先咱們要先了解,在使用 EF Core 的 CLI 時,要執行兩個步驟:數據庫
第一步:生成 Migration 文件;ide
第二步:更新變動項到數據庫;函數
既然是先生成 Migration 文件再更新,那麼在 EF Core 裏面必定有對應的模塊作這件事情。下面咱們看一下 EF Core 項目的結構。從中咱們確實找到關於 Migration 的模塊。在 Migrations/Design 目錄的類名稱上咱們能夠看出來,它就是生成 Migration 文件的。這裏先到這兒。工具
找到了生成 Migration 文件的入口,咱們再來找一下如何經過代碼將這些變動更新到數據庫中。post
在使用 EF Core 的時候,咱們都要經過繼承 DbContext 來編寫本身的 DbContext 子類。在 DbContext 類中咱們找到了一個 Database 屬性。以下圖所示:ui
而後查看了 DatabaseFacde 這個類,並無發現執行遷移相關的函數。經過代碼搜索,我在 RelationalDatabaseFacadeExtensions 這個類中有一個 Migration() 擴展方法。經過註釋的解析,我也肯定了它就是執行 Migration 文件,並將變動更新到數據庫。url
這兩個步驟對應的代碼咱們都找到了,下面咱們就編寫一段兒代碼,完成自動將模型變動更新到數據庫的功能。命令行
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 }
另一個注意點:咱們須要指定一下遷移文件所在項目。3d
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