Volo.Abp升級小記(二)建立全新微服務模組

2023-06-09 18:01:19

@


假設有一個按照官方sample搭建的微服務專案,並安裝好了abp-cli。
需要建立一個名為GDMK.CAH.Common的模組,並在模組中建立標籤管理功能

因為大部分的程式碼是自動生成的,此範例僅描述需要手動更改的內容。我們以建立一個全新的最小化的微服務模組為例,需要做以下幾步:

建立模組

用abp-cli建立一個新的模組
在專案根目錄下執行以下命令:

abp new GDMK.CAH.Common --template module --no-ui --ui='none' -d='ef' --output-folder .\modules\GDMK.CAH.Common --local-framework-ref --abp-path ..\..\..\..\abp\

--template module: 模組/服務模板
--no-ui: 不建立前端專案
-d: 指定資料庫提供程式為ef.
--output-folder: 指定輸出目錄
--local-framework-ref: 使用本地abp框架

你可以自由設定模板選項,詳情請參考abp-cli檔案

我們只需要保留應用層,領域層,資料庫,Api存取層以及各抽象層。將這些專案放在模組目錄下,即當前位置(.\modules\GDMK.CAH.Common)

Common.Host專案作為微服務單獨放置在服務目錄中(一般為microservices)

看起來模組的目錄結構如下

領域層

領域層中我們建立一個Tag實體、領域層服務和模型

Tag實體

public class Tag : AuditedEntity<long>, IMultiTenant
{
    ...
}

Tag領域層服務

public class TagManager<T> : DomainService
{
    ...
}

頁面結構看起來像這樣

應用層

在應用層抽象層中建立ITagAppService介面,以及各Dto類。

ITagAppService介面繼承ICurdAppService,實現標籤管理的增刪改查功能

public interface ITagAppService : ICurdAppService<TagDto, TagDto, long, GetAllTagInput, GetAllTagInput, CreateTagInput, CreateTagInput>, IApplicationService
{
    ...
}

頁面結構看起來像這樣

應用層中建立標籤管理的應用層服務TagAppService

TagAppService繼承CurdAppServiceBase,實現ITagAppService介面

public class TagAppService : CurdAppServiceBase<Tag, TagDto, TagDto, long, GetAllTagInput, GetAllTagInput, CreateTagInput, CreateTagInput>, ITagAppService
{
    ...
}

設定AutoMapper

在CommonApplicationAutoMapperProfile中新增Tag的對映設定

public class CommonApplicationAutoMapperProfile : Profile
{
    public CommonApplicationAutoMapperProfile()
    {
        /* You can configure your AutoMapper mapping configuration here.
         * Alternatively, you can split your mapping configurations
         * into multiple profile classes for a better organization. */
        CreateMap<Tag.Tag, TagDto>();

        CreateMap<TagDto, Tag.Tag>().Ignore(c => c.TenantId);
        CreateMap<CreateTagInput, Tag.Tag>().IgnoreAuditedObjectProperties()
                .Ignore(c => c.TenantId);

    }
}

資料庫和倉儲

在CommonDbContext中新增Tag的DbSet

public class CommonDbContext : AbpDbContext<CommonDbContext>, ICommonDbContext
{
    /* Add DbSet for each Aggregate Root here. Example:
     * public DbSet<Question> Questions { get; set; }
     */
    public DbSet<Tag.Tag> Tag { get; set; }

    ...
}

在CommonDbContextModelCreatingExtensions中新增對Tag實體的設定

ConfigureByConvention會根據DataAnnotationAttributes為實體設定一些預設的屬性,如Id為主鍵,Name為索引等,詳情請參考Abp檔案EF檔案

public static class CommonDbContextModelCreatingExtensions
{
    public static void ConfigureCommon(
        this ModelBuilder builder)
    {
        Check.NotNull(builder, nameof(builder));

        builder.Entity<Tag.Tag>(b =>
        {
            b.ToTable(CommonDbProperties.DbTablePrefix + nameof(Tag.Tag), CommonDbProperties.DbSchema);

            b.ConfigureByConvention();

        });

        ...
    }
}

在CommonEntityFrameworkCoreModule的ConfigureServices方法中,為Tag新增預設倉儲

public class CommonEntityFrameworkCoreModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        context.Services.AddAbpDbContext<CommonDbContext>(options =>
        {
            /* Add custom repositories here. Example:
             * options.AddRepository<Question, EfCoreQuestionRepository>();
             */
            options.AddDefaultRepositories(includeAllEntities: true);

        });


    }
}

控制器

新增控制器,設定路由

[Area(CommonRemoteServiceConsts.ModuleName)]
[RemoteService(Name = CommonRemoteServiceConsts.RemoteServiceName)]
[Route("api/Common/tag")]
public class TagController : CommonController<ITagAppService, TagDto, TagDto, long, GetAllTagInput, GetAllTagInput, CreateTagInput, CreateTagInput>, ITagAppService
{
    private readonly ITagAppService _tagAppService;

    public TagController(ITagAppService tagAppService) : base(tagAppService)
    {
        _tagAppService = tagAppService;
    }


}

設定微服務

在服務目錄中開啟Common.Host專案,將CommonHttpApi,CommonApplication以及CommonEntityFrameworkCore模組新增到專案參照,並建立Abp模組的依賴關係

    [DependsOn(
        typeof(AbpAutofacModule),
        typeof(AbpAspNetCoreMvcModule),
        typeof(AbpEntityFrameworkCoreSqlServerModule),
   
        typeof(CommonHttpApiModule),
        typeof(CommonApplicationModule),
        typeof(CommonEntityFrameworkCoreModule),
        ...
        )]
    public class CommonServiceHostModule : AbpModule

在launchSettings.json中指定埠號,此埠號不要跟其他服務的埠號衝突

"profiles": {  
    "CommonService.Host": {
      "commandName": "Project",
      "launchBrowser": true,
      "applicationUrl": "http://localhost:44363",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
    ...
  }

準備一些種子資料

public static List<Tag> tags = new List<Tag>()
{
    new Tag() { Title = "醫術高明" },
    new Tag() { Title = "救死扶傷" },
    new Tag() { Title = "心地仁慈" },
    new Tag() { Title = "百治百效" },
    new Tag() { Title = "白衣天使" },
    new Tag() { Title = "手到病除" },
    new Tag() { Title = "妙手回春" },
};

在CommonServiceDataSeeder建立種子資料

public class CommonServiceDataSeeder : IDataSeedContributor, ITransientDependency
{
    private readonly IRepository<Tag.Tag, long> _tagRepository;

    public CommonServiceDataSeeder(
        IRepository<Tag.Tag, long> tagRepository)
    {
        _tagRepository = tagRepository;

    }

    [UnitOfWork]
    public virtual async Task SeedAsync(DataSeedContext context)
    {
        await _tagRepository.InsertManyAsync(StaticMember.tags);
    }

}

建立遷移

將Common.Host設定為啟動專案,開啟程式包管理器控制檯選擇Common.Host預設專案。
執行Add-Migration init命令和Update-Database命令

測試微服務

啟動Common.Host,開啟瀏覽器,輸入http://localhost:44363/swagger/index.html

微服務註冊

新增資源設定

AuthServerDataSeeder中新增identityServer4資源設定

新增Scopes

private async Task CreateApiScopesAsync()
{  
    ...
    await CreateApiScopeAsync("CommonService");
}

新增Resource

private async Task CreateApiResourcesAsync()
{
    ...
    await CreateApiResourceAsync("CommonService", commonApiUserClaims);
    
}

新增Client

private async Task CreateClientsAsync()
{
    ...
    await CreateClientAsync(
            "common-service-client",
            commonScopes.Union(new[] { "InternalGateway", "IdentityService" }),
            new[] { "client_credentials" },
            commonSecret
        );
}

設定閘道器

在內外閘道器的appsettings.json中新增Ocelot對微服務的路由轉發

 {
      "DownstreamPathTemplate": "/api/common/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 44363
        }
      ],
      "UpstreamPathTemplate": "/api/common/{everything}",
      "UpstreamHttpMethod": [ "Put", "Delete", "Get", "Post" ]
    }

閘道器中新增對CommonHttpApi專案的參照,並設定Abp模組依賴

namespace BackendAdminAppGateway.Host
{
    [DependsOn(
        ...
        typeof(CommonHttpApiModule)
    )]
    public class BackendAdminAppGatewayHostModule : AbpModule

    ...

選擇啟動專案,將Common.Host微服務設定為啟動

到此完成了新模組的設定工作

執行專案

可以通過閘道器存取Tag介面了