文章系列
- 01.模組化架構入門:設計與實踐 - Part 1
- 02.模組化架構入門:領域層級 - Part 2
- 03.模組化架構入門:基礎設施層 - Part 3
- 04.模組化架構入門:應用層與 HTTP 層 - Part 4
- 05.模組化架構入門:Host 層 - Part 5
- 06.模組化架構入門:Http Client - Part 6
- 07.模組化架構入門:安裝模組 - Part 7
目錄
安裝模組
目前模組重要的部分都已經開發完成,接下來要想辦法將模組安裝到正式的應用程式上,這個部分也不困難,因為 ABP 背後會自動判斷模組間的依賴關係,
所以並不需要擔心模組的加載順序,所以只需添加專案參考與要手動將所有模組加入到 DependsOn 清單就行了。
手動安裝模組
首先建立一個新應用程式 Acme.UniStore,注意這是個應用程式專案,雖然專案的命名方式都相同,但是跟專注於抽象的模組專案不同,
可以將應用程式專案想像成最終的組裝專案,它主要的工作就是決定各種實做,例如資料庫選擇,以及加載所有關鍵模組組裝成最終應用程式。
abp new Acme.UniStore -dbms PostgreSQL --skip-migrations
這個步驟會將模組加入 Solution Folder 在添加模組過程與後續開發會比較方便。
dotnet sln Acme.UniStore.sln add ../BookStoreScratch/src/BookStoreScratch.Application -s modules/BookStoreScratch/src
dotnet sln Acme.UniStore.sln add ../BookStoreScratch/src/BookStoreScratch.Application.Contracts -s modules/BookStoreScratch/src
dotnet sln Acme.UniStore.sln add ../BookStoreScratch/src/BookStoreScratch.Domain -s modules/BookStoreScratch/src
dotnet sln Acme.UniStore.sln add ../BookStoreScratch/src/BookStoreScratch.Domain.Shared -s modules/BookStoreScratch/src
dotnet sln Acme.UniStore.sln add ../BookStoreScratch/src/BookStoreScratch.EntityFrameworkCore -s modules/BookStoreScratch/src
dotnet sln Acme.UniStore.sln add ../BookStoreScratch/src/BookStoreScratch.HttpApi -s modules/BookStoreScratch/src
dotnet sln Acme.UniStore.sln add ../BookStoreScratch/src/BookStoreScratch.HttpApi.Client -s modules/BookStoreScratch/src
將模組的各個層級依賴添加到對應到應用程式的層級內
dotnet add src/Acme.UniStore.Application/Acme.UniStore.Application.csproj reference ../BookStoreScratch/src/BookStoreScratch.Application/BookStoreScratch.Application.csproj
dotnet add src/Acme.UniStore.Application.Contracts/Acme.UniStore.Application.Contracts.csproj reference ../BookStoreScratch/src/BookStoreScratch.Application.Contracts/BookStoreScratch.Application.Contracts.csproj
dotnet add src/Acme.UniStore.Domain/Acme.UniStore.Domain.csproj reference ../BookStoreScratch/src/BookStoreScratch.Domain/BookStoreScratch.Domain.csproj
dotnet add src/Acme.UniStore.Domain.Shared/Acme.UniStore.Domain.Shared.csproj reference ../BookStoreScratch/src/BookStoreScratch.Domain.Shared/BookStoreScratch.Domain.Shared.csproj
dotnet add src/Acme.UniStore.EntityFrameworkCore/Acme.UniStore.EntityFrameworkCore.csproj reference ../BookStoreScratch/src/BookStoreScratch.EntityFrameworkCore/BookStoreScratch.EntityFrameworkCore.csproj
dotnet add src/Acme.UniStore.HttpApi/Acme.UniStore.HttpApi.csproj reference ../BookStoreScratch/src/BookStoreScratch.HttpApi/BookStoreScratch.HttpApi.csproj
dotnet add src/Acme.UniStore.HttpApi.Client/Acme.UniStore.HttpApi.Client.csproj reference ../BookStoreScratch/src/BookStoreScratch.HttpApi.Client/BookStoreScratch.HttpApi.Client.csproj
UniStoreApplicationModule 添加模組依賴 typeof(BookStoreScratchApplicationModule)
using BookStoreScratch;
using Volo.Abp.PermissionManagement;
using Volo.Abp.SettingManagement;
using Volo.Abp.Account;
using Volo.Abp.Identity;
using Volo.Abp.AutoMapper;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Modularity;
using Volo.Abp.TenantManagement;
namespace Acme.UniStore;
[DependsOn(
typeof(UniStoreDomainModule),
typeof(UniStoreApplicationContractsModule),
typeof(AbpPermissionManagementApplicationModule),
typeof(AbpFeatureManagementApplicationModule),
typeof(AbpIdentityApplicationModule),
typeof(AbpAccountApplicationModule),
typeof(AbpTenantManagementApplicationModule),
typeof(AbpSettingManagementApplicationModule),
typeof(BookStoreScratchApplicationModule)
)]
public class UniStoreApplicationModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpAutoMapperOptions>(options =>
{
options.AddMaps<UniStoreApplicationModule>();
});
}
}
UniStoreApplicationContractsModule 添加模組依賴 typeof(BookStoreScratchApplicationContractsModule)
using BookStoreScratch;
using Volo.Abp.Account;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement;
using Volo.Abp.SettingManagement;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.TenantManagement;
namespace Acme.UniStore;
[DependsOn(
typeof(UniStoreDomainSharedModule),
typeof(AbpFeatureManagementApplicationContractsModule),
typeof(AbpSettingManagementApplicationContractsModule),
typeof(AbpIdentityApplicationContractsModule),
typeof(AbpAccountApplicationContractsModule),
typeof(AbpTenantManagementApplicationContractsModule),
typeof(AbpPermissionManagementApplicationContractsModule),
typeof(BookStoreScratchApplicationContractsModule)
)]
public class UniStoreApplicationContractsModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
UniStoreDtoExtensions.Configure();
}
}
UniStoreDomainModule 添加模組依賴 typeof(BookStoreScratchDomainModule)
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Acme.UniStore.Localization;
using Acme.UniStore.MultiTenancy;
using BookStoreScratch;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.PermissionManagement.Identity;
using Volo.Abp.SettingManagement;
using Volo.Abp.BlobStoring.Database;
using Volo.Abp.Caching;
using Volo.Abp.OpenIddict;
using Volo.Abp.PermissionManagement.OpenIddict;
using Volo.Abp.AuditLogging;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.Emailing;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.TenantManagement;
namespace Acme.UniStore;
[DependsOn(
typeof(UniStoreDomainSharedModule),
typeof(AbpAuditLoggingDomainModule),
typeof(AbpCachingModule),
typeof(AbpBackgroundJobsDomainModule),
typeof(AbpFeatureManagementDomainModule),
typeof(AbpPermissionManagementDomainIdentityModule),
typeof(AbpPermissionManagementDomainOpenIddictModule),
typeof(AbpSettingManagementDomainModule),
typeof(AbpEmailingModule),
typeof(AbpIdentityDomainModule),
typeof(AbpOpenIddictDomainModule),
typeof(AbpTenantManagementDomainModule),
typeof(BlobStoringDatabaseDomainModule),
typeof(BookStoreScratchDomainModule)
)]
public class UniStoreDomainModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpMultiTenancyOptions>(options =>
{
options.IsEnabled = MultiTenancyConsts.IsEnabled;
});
#if DEBUG
context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>());
#endif
}
}
UniStoreDomainSharedModule 添加模組依賴 typeof(BookStoreScratchDomainSharedModule)
using Acme.UniStore.Localization;
using BookStoreScratch;
using Volo.Abp.AuditLogging;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.Localization;
using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Validation.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement;
using Volo.Abp.SettingManagement;
using Volo.Abp.VirtualFileSystem;
using Volo.Abp.OpenIddict;
using Volo.Abp.BlobStoring.Database;
using Volo.Abp.TenantManagement;
namespace Acme.UniStore;
[DependsOn(
typeof(AbpAuditLoggingDomainSharedModule),
typeof(AbpBackgroundJobsDomainSharedModule),
typeof(AbpFeatureManagementDomainSharedModule),
typeof(AbpPermissionManagementDomainSharedModule),
typeof(AbpSettingManagementDomainSharedModule),
typeof(AbpIdentityDomainSharedModule),
typeof(AbpOpenIddictDomainSharedModule),
typeof(AbpTenantManagementDomainSharedModule),
typeof(BlobStoringDatabaseDomainSharedModule),
typeof(BookStoreScratchDomainSharedModule)
)]
public class UniStoreDomainSharedModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
UniStoreGlobalFeatureConfigurator.Configure();
UniStoreModuleExtensionConfigurator.Configure();
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<UniStoreDomainSharedModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<UniStoreResource>("en")
.AddBaseTypes(typeof(AbpValidationResource))
.AddVirtualJson("/Localization/UniStore");
options.DefaultResourceType = typeof(UniStoreResource);
options.Languages.Add(new LanguageInfo("en", "en", "English"));
options.Languages.Add(new LanguageInfo("ar", "ar", "Arabic"));
options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "Chinese (Simplified)"));
options.Languages.Add(new LanguageInfo("zh-Hant", "zh-Hant", "Chinese (Traditional)"));
options.Languages.Add(new LanguageInfo("cs", "cs", "Czech"));
options.Languages.Add(new LanguageInfo("en-GB", "en-GB", "English (United Kingdom)"));
options.Languages.Add(new LanguageInfo("fi", "fi", "Finnish"));
options.Languages.Add(new LanguageInfo("fr", "fr", "French"));
options.Languages.Add(new LanguageInfo("de-DE", "de-DE", "German (Germany)"));
options.Languages.Add(new LanguageInfo("hi", "hi", "Hindi "));
options.Languages.Add(new LanguageInfo("hu", "hu", "Hungarian"));
options.Languages.Add(new LanguageInfo("is", "is", "Icelandic"));
options.Languages.Add(new LanguageInfo("it", "it", "Italian"));
options.Languages.Add(new LanguageInfo("pt-BR", "pt-BR", "Portuguese (Brazil)"));
options.Languages.Add(new LanguageInfo("ro-RO", "ro-RO", "Romanian (Romania)"));
options.Languages.Add(new LanguageInfo("ru", "ru", "Russian"));
options.Languages.Add(new LanguageInfo("sk", "sk", "Slovak"));
options.Languages.Add(new LanguageInfo("es", "es", "Spanish"));
options.Languages.Add(new LanguageInfo("sv", "sv", "Swedish"));
options.Languages.Add(new LanguageInfo("tr", "tr", "Turkish"));
});
Configure<AbpExceptionLocalizationOptions>(options =>
{
options.MapCodeNamespace("UniStore", typeof(UniStoreResource));
});
}
}
UniStoreHttpApiModule 添加模組依賴 typeof(BookStoreScratchHttpApiModule)
using Localization.Resources.AbpUi;
using Acme.UniStore.Localization;
using BookStoreScratch;
using Volo.Abp.Account;
using Volo.Abp.SettingManagement;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement.HttpApi;
using Volo.Abp.Localization;
using Volo.Abp.TenantManagement;
namespace Acme.UniStore;
[DependsOn(
typeof(UniStoreApplicationContractsModule),
typeof(AbpPermissionManagementHttpApiModule),
typeof(AbpSettingManagementHttpApiModule),
typeof(AbpAccountHttpApiModule),
typeof(AbpIdentityHttpApiModule),
typeof(AbpTenantManagementHttpApiModule),
typeof(AbpFeatureManagementHttpApiModule),
typeof(BookStoreScratchHttpApiModule)
)]
public class UniStoreHttpApiModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
ConfigureLocalization();
}
private void ConfigureLocalization()
{
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Get<UniStoreResource>()
.AddBaseTypes(
typeof(AbpUiResource)
);
});
}
}
UniStoreHttpApiClientModule 添加模組依賴 typeof(BookStoreScratchHttpApiClientModule)
using BookStoreScratch.HttpApi.Client;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Account;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement;
using Volo.Abp.SettingManagement;
using Volo.Abp.VirtualFileSystem;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.TenantManagement;
namespace Acme.UniStore;
[DependsOn(
typeof(UniStoreApplicationContractsModule),
typeof(AbpPermissionManagementHttpApiClientModule),
typeof(AbpFeatureManagementHttpApiClientModule),
typeof(AbpAccountHttpApiClientModule),
typeof(AbpIdentityHttpApiClientModule),
typeof(AbpTenantManagementHttpApiClientModule),
typeof(AbpSettingManagementHttpApiClientModule),
typeof(BookStoreScratchHttpApiClientModule)
)]
public class UniStoreHttpApiClientModule : AbpModule
{
public const string RemoteServiceName = "Default";
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddHttpClientProxies(
typeof(UniStoreApplicationContractsModule).Assembly,
RemoteServiceName
);
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<UniStoreHttpApiClientModule>();
});
}
}
UniStoreEntityFrameworkCoreModule 添加模組依賴 typeof(BookStoreScratchEntityFrameworkCoreModule)
using System;
using BookStoreScratch.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Uow;
using Volo.Abp.AuditLogging.EntityFrameworkCore;
using Volo.Abp.BackgroundJobs.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.PostgreSql;
using Volo.Abp.FeatureManagement.EntityFrameworkCore;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.OpenIddict.EntityFrameworkCore;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.SettingManagement.EntityFrameworkCore;
using Volo.Abp.BlobStoring.Database.EntityFrameworkCore;
using Volo.Abp.TenantManagement.EntityFrameworkCore;
using Volo.Abp.Studio;
namespace Acme.UniStore.EntityFrameworkCore;
[DependsOn(
typeof(UniStoreDomainModule),
typeof(AbpPermissionManagementEntityFrameworkCoreModule),
typeof(AbpSettingManagementEntityFrameworkCoreModule),
typeof(AbpEntityFrameworkCorePostgreSqlModule),
typeof(AbpBackgroundJobsEntityFrameworkCoreModule),
typeof(AbpAuditLoggingEntityFrameworkCoreModule),
typeof(AbpFeatureManagementEntityFrameworkCoreModule),
typeof(AbpIdentityEntityFrameworkCoreModule),
typeof(AbpOpenIddictEntityFrameworkCoreModule),
typeof(AbpTenantManagementEntityFrameworkCoreModule),
typeof(BlobStoringDatabaseEntityFrameworkCoreModule),
typeof(BookStoreScratchEntityFrameworkCoreModule)
)]
public class UniStoreEntityFrameworkCoreModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
// https://www.npgsql.org/efcore/release-notes/6.0.html#opting-out-of-the-new-timestamp-mapping-logic
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
UniStoreEfCoreEntityExtensionMappings.Configure();
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<UniStoreDbContext>(options =>
{
/* Remove "includeAllEntities: true" to create
* default repositories only for aggregate roots */
options.AddDefaultRepositories(includeAllEntities: true);
});
if (AbpStudioAnalyzeHelper.IsInAnalyzeMode)
{
return;
}
Configure<AbpDbContextOptions>(options =>
{
/* The main point to change your DBMS.
* See also UniStoreDbContextFactory for EF Core tooling. */
options.UseNpgsql();
});
}
}
這裡需要呼叫 ConfigureBookStoreScratch() 方法,將我們模組資料庫的架構整合到最終的 UniStoreDbContext。
using BookStoreScratch.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.AuditLogging.EntityFrameworkCore;
using Volo.Abp.BackgroundJobs.EntityFrameworkCore;
using Volo.Abp.BlobStoring.Database.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.Modeling;
using Volo.Abp.FeatureManagement.EntityFrameworkCore;
using Volo.Abp.Identity;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.SettingManagement.EntityFrameworkCore;
using Volo.Abp.OpenIddict.EntityFrameworkCore;
using Volo.Abp.TenantManagement;
using Volo.Abp.TenantManagement.EntityFrameworkCore;
namespace Acme.UniStore.EntityFrameworkCore;
[ReplaceDbContext(typeof(IIdentityDbContext))]
[ReplaceDbContext(typeof(ITenantManagementDbContext))]
[ConnectionStringName("Default")]
public class UniStoreDbContext :
AbpDbContext<UniStoreDbContext>,
ITenantManagementDbContext,
IIdentityDbContext
{
/* Add DbSet properties for your Aggregate Roots / Entities here. */
#region Entities from the modules
/* Notice: We only implemented IIdentityProDbContext and ISaasDbContext
* and replaced them for this DbContext. This allows you to perform JOIN
* queries for the entities of these modules over the repositories easily. You
* typically don't need that for other modules. But, if you need, you can
* implement the DbContext interface of the needed module and use ReplaceDbContext
* attribute just like IIdentityProDbContext and ISaasDbContext.
*
* More info: Replacing a DbContext of a module ensures that the related module
* uses this DbContext on runtime. Otherwise, it will use its own DbContext class.
*/
// Identity
public DbSet<IdentityUser> Users { get; set; }
public DbSet<IdentityRole> Roles { get; set; }
public DbSet<IdentityClaimType> ClaimTypes { get; set; }
public DbSet<OrganizationUnit> OrganizationUnits { get; set; }
public DbSet<IdentitySecurityLog> SecurityLogs { get; set; }
public DbSet<IdentityLinkUser> LinkUsers { get; set; }
public DbSet<IdentityUserDelegation> UserDelegations { get; set; }
public DbSet<IdentitySession> Sessions { get; set; }
// Tenant Management
public DbSet<Tenant> Tenants { get; set; }
public DbSet<TenantConnectionString> TenantConnectionStrings { get; set; }
#endregion
public UniStoreDbContext(DbContextOptions<UniStoreDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
/* Include modules to your migration db context */
builder.ConfigurePermissionManagement();
builder.ConfigureSettingManagement();
builder.ConfigureBackgroundJobs();
builder.ConfigureAuditLogging();
builder.ConfigureFeatureManagement();
builder.ConfigureIdentity();
builder.ConfigureOpenIddict();
builder.ConfigureTenantManagement();
builder.ConfigureBlobStoring();
builder.ConfigureBookStoreScratch();
/* Configure your own tables/entities inside here */
//builder.Entity<YourEntity>(b =>
//{
// b.ToTable(UniStoreConsts.DbTablePrefix + "YourEntities", UniStoreConsts.DbSchema);
// b.ConfigureByConvention(); //auto configure for the base class props
// //...
//});
}
}
接下來設定資料庫連線字串,分別需要修改 DbMigrator 與 Web 專案的 appsettings.json,這裡的 Web 專案預設是一體式架構,
最終完整的應用就是靠 Web 專案運行。
"ConnectionStrings": {
"Default": "Host=localhost;Port=5432;Database=UniStore;User ID=postgres;Password=myPassw0rd;"
}
回到 Acme.UniStore.EntityFrameworkCore 專案添加遷移腳本,注意跟我們模組的 EntityFrameworkCore 的概念不完全一樣,
這裡代表的是最終應用的 EntityFrameworkCore 所以在這邊決定使用 PostgreSql 實做與預設安裝 EntityFrameworkCore.Tools。
dotnet ef migrations add "Initial"
完成後運行 Acme.UniStore.DbMigrator 建立新的資料並運行 Seeder,此時我們的書店資料表也會加入,並且自動運行之前寫的 Seeder 插入一筆資料。
最後啟動 Acme.UniStore.Web 並查看 Swagger,會發現我們的 Book API 已經正式加入了,並且能讀取到一筆預設資料。
GET https://localhost:44320/api/book
Response body
{
"totalCount": 1,
"items": [
{
"name": "The Hitchhiker's Guide to the Galaxy",
"bookType": 7,
"publishDate": "1979-10-12T00:00:00",
"price": 42,
"id": "3a1e8043-d1e8-b17f-1382-9d6dc31b3832"
}
]
}
自動安裝模組
實際手動安裝過一次模組後會發現幾乎都是複製貼上而已,因此有更快的方式就是透過命令安裝 abp install-local-module。
要使用這個命令前要先改造一下我們的模組,因為 ABP 模組安裝命令原理是透過兩種類型的檔案 abppkg 與 abpmdl,
abppkg: 每個模組層級都要定義一個 abppkg,用來說明此函式庫是什麼層級。
abpmdl: 需要放在方案同一層級中,它是作為藍圖的角色,ABP 命令需要透過它才知道這個模組內部的 abppkg 清單。
例如在 BookStoreScratch.Application 的專案同一層級下定義一個 BookStoreScratch.Application.abppkg 內容為:
{
"role": "lib.application"
}
這個 role 是由 ABP 自己定義的,當命令看到是這個角色時會將模組的 Application 專案參考添加到應用程式的 Application專案中,
並且在 DependsOn 清單中加上我們的 BookStoreScratchApplicationModule,等於是使用此命令能把我們之前手動操作都處理掉了。
接下來在所有層級都定義 abppkg,BookStoreScratch.Application.Contracts 專案,新增 BookStoreScratch.Application.Contracts.abppkg
{
"role": "lib.application-contracts"
}
BookStoreScratch.Domain 專案,新增 BookStoreScratch.Domain.abppkg
{
"role": "lib.domain"
}
BookStoreScratch.Domain.Shared 專案,新增 BookStoreScratch.Domain.Shared.abppkg
{
"role": "lib.domain-shared"
}
BookStoreScratch.EntityFrameworkCore 專案,新增 BookStoreScratch.EntityFrameworkCore.abppkg
{
"role": "lib.ef"
}
BookStoreScratch.HttpApi 專案,新增 BookStoreScratch.HttpApi.abppkg
{
"role": "lib.http-api"
}
BookStoreScratch.HttpApi.Client 專案,新增 BookStoreScratch.HttpApi.Client.abppkg
{
"role": "lib.http-api-client"
}
完成後回到方案根目錄,並新增 BookStoreScratch.abpmdl
{
"folders": {
"items": {
"src": {},
"test": {}
}
},
"packages": {
"BookStoreScratch.HttpApi": {
"path": "src/BookStoreScratch.HttpApi/BookStoreScratch.HttpApi.abppkg",
"folder": "src"
},
"BookStoreScratch.HttpApi.Client": {
"path": "src/BookStoreScratch.HttpApi.Client/BookStoreScratch.HttpApi.Client.abppkg",
"folder": "src"
},
"BookStoreScratch.Application": {
"path": "src/BookStoreScratch.Application/BookStoreScratch.Application.abppkg",
"folder": "src"
},
"BookStoreScratch.Application.Contracts": {
"path": "src/BookStoreScratch.Application.Contracts/BookStoreScratch.Application.Contracts.abppkg",
"folder": "src"
},
"BookStoreScratch.Domain": {
"path": "src/BookStoreScratch.Domain/BookStoreScratch.Domain.abppkg",
"folder": "src"
},
"BookStoreScratch.Domain.Shared": {
"path": "src/BookStoreScratch.Domain.Shared/BookStoreScratch.Domain.Shared.abppkg",
"folder": "src"
},
"BookStoreScratch.EntityFrameworkCore": {
"path": "src/BookStoreScratch.EntityFrameworkCore/BookStoreScratch.EntityFrameworkCore.abppkg",
"folder": "src"
}
}
}
完成後在新增一個新的測試應用程式
abp new Acme.UniStore1 -dbms PostgreSQL --skip-migrations
最後透過 abp install-local-module 讀取 BookStoreScratch.abpmdl 模組安裝到 Acme.UniStore1 底下
abp install-local-module ../BookStoreScratch/BookStoreScratch.abpmdl -t Acme.UniStore1.abpmdl
執行完後會發現所有添加參考與添加 DependsOn 的工作全部都自動處理了,只剩下設定資料庫與連線字串需要手動設定,最後就能運行專案並且結果與第一個應用相同。
今天的進度 Github