EF Core Migration 可依據 Entity 類別針對不同資料庫(MSSQL、SQLite、Oracle、MySQL...)產生對映的 CREATE TABLE Script,能自動連上資料庫伺服器建好資料表,Entity 修改時還能生出增減修改欄位 ALTER Script (但有一些限制) 套用資料庫直接改版... 是我心中 EF Core 最迷人的特點之一。(另一項很殺的特色 - 改兩行程式,資料庫便能從 Oracle 換成 MSSQL)

不過,之前範例都是單一專案,Entity 類別寫在 ASP.NET Core 專案裡,用哪一種資料庫、連線字串是啥寫在 ASP.NET Core 的 Startup.cs,要建立 EF Core Migration 就在 ASP.NET Core 專案下指令,沒什麼大問題。

實際開發較有規模的專案時,資料庫相關程式常會被分離成類別程式庫 (Class Library),這種情境下建立 EF Core Migration 有一些額外步驟。

  1. 在 DbContext 所在的類別程式庫新增一個型別實作 IDesignTimeDbContextFactory<YourDbContextType>,宣告一個 CreateDbContext() 方法建立 YourDbContextType 物件並傳回,在其中決定使用哪一種資料庫、連線字串為何,EF Core 才知道怎麼產生 SQL Script 及更新資料庫。
    這個 IDesignTimeDbContextFactory 介面物件會在執行 dotnet ef migrations ... 時被呼叫,若不想將連線字串寫死在程式裡,放進 appSettings.json 裡是個好選擇(還可與網站或應用程式共用讀取及解密連線字串邏輯)。類別程式庫專案要讀 appsettings.json 需引用 Microsoft.Extensions.Configuration.Json NuGet Package,並透過 ConfigurationBuilder() 載入,以下是個範例:()

    public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<ServiceDbContext>
    {
        public ServiceDbContext CreateDbContext(string[] args)
        {
            var config = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory()) //會在類別程式庫專案執行,由同目錄取JSON
                .AddJsonFile("appSettings.json")
                .Build();
    
            var cnStr = config.GetConnectionString("MYDB"); //TODO: 正式應用時且連線字串含密碼時應加密儲存
            if (string.IsNullOrEmpty(cnStr)) throw new ApplicationException("Missing .config connection string [MYDB]");
            var ctxBuilder = new DbContextOptionsBuilder();
            ctxBuilder.UseSqlite(cnStr);
            return new ServiceDbContext(ctxBuilder.Options);
        }
    }
    
  2. 在類別程式庫放入 appsettings.json:

    {
      "ConnectionStrings": {
        "MYDB": "data source=MyDB.db"
      }
    }
    
  3. 直接在類別程式庫專案執行 dotnet ef migrations add InitCreate 會冒出以下錯誤:

    Startup project 'MyClassLib.csproj' targets framework '.NETStandard'. There is no runtime associated with this framework, and projects targeting it cannot be executed directly. To use the Entity Framework Core .NET Command-line Tools with this project, add an executable project targeting .NET Core or .NET Framework that references this project, and set it as the startup project using --startup-project; or, update this project to cross-target .NET Core or .NET Framework. For more information on using the EF Core Tools with .NET Standard projects, see https://go.microsoft.com/fwlink/?linkid=2034781

    理由是 EF 工具必須靠 EXE 或 ASP.NET 網頁執行建立 Process 載入類別程式庫運作,因此要加上 --startup-project 指向主控台或網站專案所在資料夾,例如: dotnet ef migrations add InitCreate --startup-project ..\web_or_console_app_project_folder
    提醒:--startup-project 所指的專案需參照 Microsoft.EntityFramework.Core.Design NuGet Package.

這樣,就能用專屬的 appsettings.json 決定連線 DB,在類別程式庫中建立 EF Core Migration 類別跟 SQL Script 了。

【延伸閱讀】

Tips of how to use EF Core Migragtions in class library.


Comments

# by rich

var ctxBuilder = new DbContextOptionsBuilder(); 少了型別的對應 var ctxBuilder = new DbContextOptionsBuilder<ServiceDbContext>();

Post a comment