如何使用 abp 建立 module 並應用單獨的資料庫遷移

烈火寒冰發表於2024-04-19

建立 abp 專案

官方文件已經提供了非常詳細的新建專案嚮導。參考:https://docs.abp.io/en/abp/latest/Getting-Started-Create-Solution?UI=Blazor&DB=EF&Tiered=Yes

CLI 命令參考:https://docs.abp.io/en/abp/latest/CLI

我們使用 abp CLI 建立一個新專案。我使用 Blazor 來開發前端頁面,正好學一下。

abp new MainApp -u blazor

然後按照官方文件操作即可:https://docs.abp.io/en/abp/latest/Getting-Started-Running-Solution?UI=Blazor&DB=EF&Tiered=Yes

主要步驟是:

  • 修改資料庫連線字串

  • 執行 .DbMigrator 專案初始化資料庫並填充初始資料。

  • 執行 .HttpApi.Host 專案

如果操作正確,我們應該能夠看到 Swagger 文件了。

如果同時啟動 .HttpApi.Host 和 .Blazor 專案,前端頁面也應該可以正常展示。基本的使用者登入、角色許可權都有了。

建立一個新模組

剛開始我是用 CLI 建立了一個新模組,然後手動把兩個專案的 solution 檔案整合到一塊,麻煩而且易出錯。後來發現原來 CLI 已經提供了將新模組新增到現有解決方案的命令,可參考:https://docs.abp.io/en/abp/latest/CLI#options-3

可以使用以下的命令:

abp add-module MyModule --new --add-to-solution-file

注意執行該命令的時候要在 MainApp 的目錄下。這樣 CLI 會在當前目錄下建立一個 modules 目錄來存放新模組的解決方案,同時把所有專案新增到 MainApp 的解決方案中,還會修改一些檔案,這樣我們就不用手動整合模組了。

新增新 Model

接下來繼續按照官方文件來新增新的 Model:https://docs.abp.io/en/abp/latest/Tutorials/Part-1

具體內容這裡就不展開了,可根據實際需要新增幾個 Model。

步驟大概是:

  • 新增 Model

  • 更新模組 MyModule.EntityFrameworkCore 專案中的 MyModuleDbContext.cs 檔案,新增相應的 DbSet

  • 在 MyModuleDbContextModelCreatingExtensions.cs 中新增模型對映

為模組準備資料庫遷移

接下來就是本文的重點,如何為模組新增一個單獨的資料庫並實現資料庫遷移。

首先在 MainApp.EntityFrameworkCore 專案中新增目錄 EntityFrameworkCore\MyModule。在這個目錄中建立一個名為 MyModuleMigrationsDbContext.cs 的檔案。內容如下:

public class MyModuleMigrationsDbContext : AbpDbContext<MyModuleMigrationsDbContext>
    {
        public MyModuleMigrationsDbContext(DbContextOptions<MyModuleMigrationsDbContext> options) : base(options)
        {
        }
 
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
 
            modelBuilder.ConfigureMyModule();
        }
    }

然後新增一個名為 MyModuleMigrationsDbContextFactory.cs 的檔案:

public class MyModuleMigrationsDbContextFactory : IDesignTimeDbContextFactory<MyModuleMigrationsDbContext>
    {
        public MyModuleMigrationsDbContext CreateDbContext(string[] args)
        {
            var configuration = BuildConfiguration();
 
            var builder = new DbContextOptionsBuilder<MyModuleMigrationsDbContext>()
                .UseSqlServer(configuration.GetConnectionString("MyModule"));
            return new MyModuleMigrationsDbContext(builder.Options);
        }
 
        private static IConfigurationRoot BuildConfiguration()
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), "../MainApp.DbMigrator/"))
                .AddJsonFile("appsettings.json", optional: false);
 
            return builder.Build();
        }
    }

這兩個檔案與 MainApp 的 DbContext 內容是類似的,大家可以比較一下。因為模組也要從 appsettings.json 中讀取資料庫配置,因此我們需要修改 appsettings.json 中的資料庫連結字串,注意需要修改兩個位置:一個是 MainApp.DbMigrator 專案中的,另一個是 MainApp.HttpApi.Host 專案中的。仿照 MainApp 的連線字串新增一個名為 MyModule 的即可。這樣模組就可以有一個單獨的資料庫了。

執行模組資料庫遷移

建立了新的 Model 後,我們要對資料進行遷移,以應用這些更改。

將 terminal 導航到 MainApp.EntityFrameworkCore 目錄,執行以下命令:

dotnet ef migrations add migrationName --context MyModuleMigrationsDbContext --output-dir Migrations/MyModule

注意要指定要遷移的 DbContext,也就是 MyModuleMigrationsDbContext。同時我們還將遷移檔案放在 Migrations/MyModule 目錄下,這樣不會與 MainApp 的遷移檔案混淆。

如果一切正常,我們就可以在指定目錄下看到生成的遷移檔案了。

執行以下命令以應用資料庫遷移:

dotnet ef database update --context MyModuleMigrationsDbContext

這樣模組的資料庫遷移就完成了。

其他設定

接下來按照官方文件繼續新增相應的 AppService 及其介面等。這樣我們的模組已經與 MainApp 是隔離的了,包括 Blazor 頁面也是在一個單獨的專案裡。

如果執行 .HttpApi.Host 沒有看到模組的 API,可以檢查下 MainApp.HttpApi.Host 專案中的 MainAppHttpApiHostModule.cs 檔案,看是否配置正確:

private void ConfigureConventionalControllers()
    {
        Configure<AbpAspNetCoreMvcOptions>(options =>
        {
            options.ConventionalControllers.Create(typeof(MainAppApplicationModule).Assembly);
            // Add the below line
            options.ConventionalControllers.Create(typeof(MyModuleApplicationModule).Assembly);
        });
    }

再次執行 .HttpApi.Host 和 .Blazor 專案,可以看到前端頁面已經新增了一個新模組的連結,只是內容頁是空的。接下來我們就可以在 MyModule 解決方案裡的 Blazor 專案中開發前端頁面了。

在新增 Model 後我還遇到過一個錯誤,如下圖所示:

Autofact.Core.DependencyResolutionException: 'An exception was thrown while activating Volo.Abp.IdentityServer.Grants.PersistedGrantStore ....'

乍一看還以為是 IdentityServer 的問題,其實不是。展開看 inner exception 就會發現這個是我們新增的 Dto 的 mapping 導致的。因為 Entity 是有 Id 的,但 CreateUpdateProductDto 沒有Id,所以 mapping 出錯了。如果遇到這種情況,可以將 Id 省略掉:

CreateMap<CreateUpdateProductDto, Product>().Ignore(x => x.Id);

學習了幾天感覺這個框架還是挺方便的,功能非常強大,但學習起來還是有一定難度的。框架各部分之間的依賴非常多,如果不仔細研究,很容易處處碰壁。好在官方文件寫的還是相對比較詳細的,只是部分文件還有空缺。希望官方以後補足吧。是否使用框架開發是一個兩難的問題,太依賴框架可能會失去對細節的把控,處處受制於框架的實現;不使用框架就得自己重複造輪子。只能見仁見智了。

相關文章