傳統的設定再執行 Add-Migration 會出現錯誤:
PM> Add-Migration InitialCreation
Unable to create an object of type ‘OrderContext’. Add an implementation of ‘IDesignTimeDbContextFactory<OrderContext>’ to the project, or see https://go.microsoft.com/fwlink/?linkid=851728 for additional patterns supported at design time.
這是因為 EF Core 2.0 有改變 Tools 的作法,在 Asp.net core 最佳的解法是:
WebHost.CreateDefaultBuilder(args)
如果是 Console program,可以用 emtpy constructor 的方式:
public class OrderContext : DbContext { public OrderContext() { } public OrderContext(DbContextOptions<OrderContext> options) : base(options) { } public DbSet<OrderProcess> OrderProcesses { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"Server=(localdb)\MSSQLLocalDB;Database=Msmtest;Trusted_Connection=True;"); } }
重點在於 OnConfiguring 加上指定的 SqlServer connection。一旦產生 Migration 之後,就可以將 empty constructor comment 掉(因為可能會影響程式的正常邏輯)。
如果有多個 Context,可以在 Add-migration & Update-Database 中指定哪一個要進行更新:
Add-Migration Initial -Context LhcPlatformContext Update-Database -Context LhcPlatformContext
請注意,如果使用 Seeding the Database 進行資料庫 seeding,那要注意必須要先拿掉 seed data 段落之後才會正確執行,否則會出現: “An error occurred while calling method ‘BuildWebHost’ on class ‘Program’. Continuing without the application service provider.
這個原因是因為我們都是使用 Development 環境在執行,因此會進入到 Seed() 函數,但在 Add-migration 時候實際上並沒有對應的資料庫表格,因此在執行 Seed() 會造成錯誤。
解學方案就是透過檢查 Migration 是否都已經執行後,在進行 Seed,。詳細作法參閱:How to seed your EF Core database
重點在於檢查是否都已經執行 migration:
using (var scope = app.ApplicationServices.CreateScope()) { var services = scope.ServiceProvider; if (!services.GetService<LhcPlatformContext>().AllMigrationsApplied()) { var context = services.GetRequiredService<LhcPlatformContext>(); context.Database.Migrate(); LhcSeedData.Initializer(context); } }
其中 AllMigrationsApplied() 如下:
public static bool AllMigrationsApplied(this DbContext context) { var applied = context.GetService<IHistoryRepository>() .GetAppliedMigrations() .Select(m => m.MigrationId); var total = context.GetService<IMigrationsAssembly>() .Migrations .Select(m => m.Key); return !total.Except(applied).Any(); }