本文首要讲解了怎么把ABP官方的在线生成解决方案运转起来,并阐明晰解决方案中项目间的依靠联系。然后手动实践了怎么从0建立了一个简化的解决方案。ABP官方的在线生成解决方案源码下载参阅[3],手动建立的简化的解决方案源码下载参阅[4]。
一.ABP官方在线生成解决方案
1.将在线生成解决方案跑起来
首要进入页面abp.io/get-started…
然后头脑中要有一个项目之间的依靠联系图,不清楚的可以参阅《根据ABP完成DDD》:
截止目前为止,项目运用的.NET版别是6.0,ABP版别是5.3.3。
运用Rider翻开项目Acme.BookStore后,会提示运用yarn安装package,安装包后:
在整个解决方案中查找ConnectionStrings,发现其在Acme.BookStore.HttpApi.Host、Acme.BookStore.DbMigrator和Acme.BookStore.IdentityServer这3个发动项目中呈现:
将ConnectionStrings中的内容替换为:
“ConnectionStrings”: {
“Default”: “Server=127.0.0.1;Database=BookStore;Trusted_Connection=True;User ID=sa;Password=913292836;”
},
然后开端运转Acme.BookStore.DbMigrator进行数据种子搬迁:
呈现上面图片输出成果,基本上表明搬迁成功完成了,接下来看看数据库:
运转Acme.BookStore.Web项目如下:
发动后发现报错了,发现首要是3个问题:一个是Redis没有发动,另一个问题是IDS4服务没有发动,最后一个问题是Acme.BookStore.HttpApi.Host没有发动:
发动IDS4服务的时分发现报错Volo.Abp.AbpException: Could not find the bundle file ‘/libs/abp/core/abp.css’ for the bundle ‘Basic.Global’!:
在该项目下履行指令abp install-libs:
发现消息提示说ABP CLI有个更新的5.3.3版别,经过指令dotnet tool update -g Volo.Abp.Cli进行升级。再次运转Acme.BookStore.IdentityServer项目,发现不报过错了。如果在发动其它项目(特指Acme.BookStore.Web项目)的时分报同样的过错,那么同样履行指令abp install-libs即可解决问题。一起发动这3个项目如下:
发动成功后就可以见到了解的界面:
下面是项目Swagger的界面:
至此,现已把从官方下载下来的项目成功地运转起来了。
2.ABP运转流程
下面是在网上[1]找到的一张图,很清晰的阐明晰AspNet Core和ABP模块的运转流程,个人认为图上的Startup.ConfigureServices应该是Startup.Configure,现已在图中做了修改。
(1)AspNet Core运转流程
简略了解,基本上便是在Startup.ConfigureServices中进行依靠注入装备,然后在Startup.Configure中装备管道中间件,拜访的时分就像洋葱模型。
(2)ABP模块运转流程
在ABP模块中对Startup.ConfigureServices做了扩展,增加了PreConfigureServices和PostConfigureServices。对Startup.Configure也做了扩展,当然姓名也修改了,Startup.Configure适当所以OnApplicationInitialization,一起增加了OnPreApplicationInitialization和OnPostApplicationInitialization。
在ABP解决方案中有多个项目,每个项目都会有一个类承继自AbpModule。而且经过DependsOn描述了该模块依靠的模块。这样在ABP解决方案中就会有很多模块之间的依靠联系,经过拓扑排序算法对模块进行排序,从最深层的模块依次加载,直到发动一切模块。
(3)AbpModule抽象类中的办法
除了首要的PreConfigureServices()、ConfigureServices()、PostConfigureServices()、OnPreApplicationInitialization()、OnApplicationInitialization()、OnPostApplicationInitialization()办法外,还有一些其它的办法。abp\framework\src\Volo.Abp.Core\Volo\Abp\Modularity\AbpModule.cs:
二.手动创立解决方案
0.创立解决方案
首要创立一个目录BookStoreHand用于存放解决方案:
然后创立一个解决方案,履行指令dotnet new sln -n Acme.BookStore:
用Rider翻开后解决方案是空的,然后手动创立2个New Solution Folder,分别为src和test:
1.创立范畴同享层和范畴层
(1)Acme.BookStore.Domain.Shared[范畴同享层]
一般界说的常量和枚举,都放在该项目中。经过指令dotnet new classlib -n Acme.BookStore.Domain.Shared和dotnet sln ../Acme.BookStore.sln add Acme.BookStore.Domain.Shared创立范畴同享层,并将其增加到解决方案当中:
然后便是创立模块类BookStoreDomainSharedModule如下:
namespace Acme.BookStore.Domain.Shared
{
public class BookStoreDomainSharedModule: AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
base.ConfigureServices(context);
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
base.OnApplicationInitialization(context);
}
}
}
阐明:接下来创立项目、增加项目间的引证等都运用Rider,而不再运用CLI操作,觉得CLI并不便利操作。基本思路顺序都是:创立项目,设置引证联系,创立模块,其它操作。
(2)Acme.BookStore.Domain[范畴层]
该项目包括实体、值目标、范畴服务、规约、仓储接口等。经过Rider创立Class Library项目Acme.BookStore.Domain如下:
Acme.BookStore.Domain项目依靠于Acme.BookStore.Domain.Shared项目:
创立模块类BookStoreDomainSharedModule如下:
[DependsOn(
typeof(BookStoreDomainSharedModule) //依靠范畴同享模块
)]
public class BookStoreDomainModule: AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
base.ConfigureServices(context);
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
base.OnApplicationInitialization(context);
}
}
创立范畴实体Book:
public class Book: Entity
{
public string BookName { get; set; } //姓名
public string Author { get; set; } //作者
public DateTime PublishDate { get; set; } //出版日期
public double Price { get; set; } //价格
}
创立仓储IBookRepository接口:
public interface IBookRepository: IRepository<Book, int>
{
}
2.创立基础设施层
(1)创立项目
基础设施层Acme.BookStore.EntityFrameworkCore是EF Core中心基础依靠项目,包括数据上下文、数据库映射、EF Core仓储完成等。经过Rider创立Class Library项目Acme.BookStore.EntityFrameworkCore如下:
Acme.BookStore.EntityFrameworkCore项目依靠于Acme.BookStore.Domain项目:
(2)创立模块
创立模块类BookStoreEntityFrameworkCoreModule如下:
[DependsOn(
typeof(BookStoreDomainModule),
typeof(AbpEntityFrameworkCoreSqlServerModule)
)]
public class BookStoreEntityFrameworkCoreModule: AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext(options =>
{
// 给一切的实体都增加默认仓储
options.AddDefaultRepositories(includeAllEntities: true);
});
Configure<AbpDbContextOptions>(options =>
{
options.UseSqlServer();
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
base.OnApplicationInitialization(context);
}
}
(3)创立数据库上下文
创立数据库上下文BookStoreDbContext:
public class BookStoreDbContext: AbpDbContext
{
public BookStoreDbContext(DbContextOptions options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(typeof(BookStoreDbContext).Assembly);
}
}
ApplyConfigurationsFromAssembly运用来自IEntityTypeConfiguration中的装备。界说实体映射BookDbMapping如下:
public class BookDbMapping: IEntityTypeConfiguration
{
public void Configure(EntityTypeBuilder builder)
{
// 装备主键
builder.HasKey(b => b.Id).HasName(“Id”);
// 装备表和字段
builder.ToTable("AbpBook");
builder.Property(t => t.BookName).IsRequired().HasColumnName("BookName").HasComment("书名");
builder.Property(t => t.Author).IsRequired().HasColumnName("Author").HasComment("作者");
builder.Property(t => t.PublishDate).IsRequired().HasColumnName("PublishDate").HasComment("出版日期");
builder.Property(t => t.Price).IsRequired().HasColumnName("Price").HasComment("价格");
// 装备联系
}
}
(4)创立仓储完成
界说IBookRepository的完成BookRepository如下:
public class BookRepository: EfCoreRepository<BookStoreDbContext, Book, int>, IBookRepository
{
public BookRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider)
{
}
}
3.创立运用契约层和运用层
(1)Acme.BookStore.Application.Contracts[运用契约层]
包括运用服务接口和数据传输目标。该项⽬被应⽤程序客户端引证,比如Web项目、API客户端项目。经过Rider创立Class Library项目Acme.BookStore.Application.Contracts:
Acme.BookStore.Application.Contracts项目依靠于Acme.BookStore.Domain.Shared项目如下:
创立模块类BookStoreApplicationContractsModule如下:
[DependsOn(
typeof(BookStoreDomainSharedModule), //依靠于BookStoreDomainSharedModule
typeof(AbpObjectExtendingModule) //依靠于AbpObjectExtendingModule
)]
public class BookStoreApplicationContractsModule: AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
base.ConfigureServices(context);
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
base.OnApplicationInitialization(context);
}
}
创立服务接口IBookAppService如下:
public interface IBookAppService: IApplicationService
{
///
///
Task GetBookAsync(int id);
}
创立输出DTO为BookDto如下:
public class BookDto
{
public int Id { get; set; } //主键
public string BookName { get; set; } //姓名
public string Author { get; set; } //作者
public DateTime PublishDate { get; set; } //出版日期
public double Price { get; set; } //价格
}
(2)Acme.BookStore.Application[运用层]
完成在Contracts项目中界说的接⼝。经过Rider创立Class Library项目Acme.BookStore.Application如下:
Acme.BookStore.Application项目依靠于
Acme.BookStore.Application.Contracts和Acme.BookStore.Domain项目:
创立模块类BookStoreApplicationModule如下:
[DependsOn(
typeof(AbpAutoMapperModule), //依靠于AutoMapper
typeof(BookStoreDomainModule), //依靠于BookStoreDomainModule
typeof(BookStoreApplicationContractsModule) //依靠于BookStoreApplicationContractsModule
)]
public class BookStoreApplicationModule: AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var services = context.Services;
// 增加ObjectMapper注入
services.AddAutoMapperObjectMapper();
// Abp AutoMapper设置
Configure<AbpAutoMapperOptions>(config =>
{
config.AddMaps<BookStoreApplicationAutoMapperProfile>();
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
base.OnApplicationInitialization(context);
}
}
创立自动映类BookStoreApplicationAutoMapperProfile如下:
public class BookStoreApplicationAutoMapperProfile: Profile
{
public BookStoreApplicationAutoMapperProfile()
{
CreateMap<BookDto, Book>();
CreateMap<Book, BookDto>();
}
}
创立IBookAppService类的完成类BookAppService如下:
public class BookAppService: ApplicationService, IBookAppService
{
private readonly IBookRepository _bookRepository;
public BookAppService(IBookRepository bookRepository)
{
_bookRepository = bookRepository;
}
public async Task<BookDto> GetBookAsync(int id)
{
var queryable = await _bookRepository.GetQueryableAsync();
var book = queryable.FirstOrDefault(t => t.Id == id);
if (book == null)
{
throw new ArgumentNullException(nameof(book));
}
return ObjectMapper.Map<Book, BookDto>(book);
}
}
4.创立种子搬迁
Acme.BookStore.DbMigrator是操控台运用程序,首要是搬迁数据库结构并初始化种子数据。经过Rider创立ASP.NET Core Web Application的Empty项目Acme.BookStore.DbMigrator如下:
Acme.BookStore.DbMigrator项目依靠于Acme.BookStore.Application.Contracts和Acme.BookStore.EntityFrameworkCore项目如下:
创立模块类BookStoreDbMigratorModule如下:
[DependsOn(
typeof(AbpAutofacModule),
typeof(BookStoreEntityFrameworkCoreModule),
typeof(BookStoreApplicationContractsModule)
)]
public class BookStoreDbMigratorModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
base.ConfigureServices(context);
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
base.OnApplicationInitialization(context);
}
}
在Program.cs中增加services.AddHostedService()这个数据库搬迁主机服务,在DbMigratorHostedService类中包括StartAsync()和StopAsync()这2个办法,在StartAsync()中获取BookStore数据库搬迁服务BookStoreDbMigrationService,并履行数据库搬迁办法MigrateAsync()。数据库搬迁的思路基本上便是在Acme.BookStore.DbMigrator目录下履行指令:dotnet ef migrations add InitialCreate和dotnet ef database update,只不过运用的C#代码来完成的。自己经过Acme.BookStore.DbMigrator项目没有搬迁成功,最后仍是经过指令行完成搬迁的。搬迁成果如下:
阐明:Program.cs、DbMigratorHostedService.cs和BookStoreDbMigrationService.cs的源码等完好项目源码参阅[4]。
5.创立长途服务层
Acme.BookStore.HttpApi[长途服务层],简略了解便是很薄的操控层,该项目首要用于界说HTTP API,即运用服务层的包装器,将它们公开给长途客户端调用。经过Rider创立Class Library项目Acme.BookStore.HttpApi如下:
Acme.BookStore.HttpApi项目依靠于Acme.BookStore.Application.Contracts项目如下:
创立模块类BookStoreHttpApiModule如下:
[DependsOn(
typeof(BookStoreApplicationContractsModule) //依靠于BookStoreApplicationContractsModule
)]
public class BookStoreHttpApiModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
base.ConfigureServices(context);
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
base.OnApplicationInitialization(context);
}
}
为了扼要阐明问题,创立一个简略的操控器类BookStoreController如下:
[RemoteService]
[Area(“BookStore”)]
[Route(“api/app/book”)]
public class BookStoreController: AbpControllerBase
{
private readonly IBookAppService _bookAppService;
public BookStoreController(IBookAppService bookAppService)
{
_bookAppService = bookAppService;
}
[HttpGet]
[Route("get-book")]
public Task<BookDto> GetBookAsync(int id)
{
return _bookAppService.GetBookAsync(id);
}
}
6.创立展示层
Acme.BookStore.HttpApi.Host这个是前后端别离时的项目命名方法。经过Rider创立ASP.NET Core Web Application的Empty项目Acme.BookStore.HttpApi.Host如下:
Acme.BookStore.HttpApi.Host项目依靠于Acme.BookStore.Application、Acme.BookStore.EntityFrameworkCore和Acme.BookStore.HttpApi项目如下:
创立模块类BookStoreHttpApiHostModule如下:
[DependsOn(
typeof(BookStoreHttpApiModule), //依靠于BookStoreHttpApiModule
typeof(AbpAutofacModule), //依靠于AbpAutofacModule
typeof(BookStoreApplicationModule), //依靠于BookStoreApplicationModule
typeof(BookStoreEntityFrameworkCoreModule), //依靠于BookStoreEntityFrameworkCoreModule
typeof(AbpAspNetCoreSerilogModule), //依靠于AbpAspNetCoreSerilogModule
typeof(AbpSwashbuckleModule) //依靠于AbpSwashbuckleModule
)]
public class BookStoreHttpApiHostModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var services = context.Services;
var configuration = services.GetConfiguration();
ConfigureConventionalControllers();
ConfigureCors(context, configuration);
ConfigureSwaggerServices(context, configuration);
}
private void ConfigureConventionalControllers()
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(BookStoreApplicationModule).Assembly);
});
}
private void ConfigureCors(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddCors(options =>
{
options.AddPolicy("AllowAll",builder =>
{
builder
.WithOrigins(
configuration["App:CorsOrigins"]
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.RemovePostFix("/"))
.ToArray()
)
.WithAbpExposedHeaders()
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
}
private static void ConfigureSwaggerServices(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "BookStore API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var env = context.GetEnvironment();
var app = context.GetApplicationBuilder();
var configuration = context.GetConfiguration();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors("AllowAll");
if (configuration["UseSwagger"] == "true")
{
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Acme.BookStore API");
});
}
app.UseRouting();
app.UseConfiguredEndpoints();
}
}
阐明:HomeController.cs、Program.cs和装备文件等项目完好源码参阅[4]。
将Acme.BookStore.HttpApi.Host项目发动起来后如下:
经过Swagger界面测验https://localhost:7016/api/app/book/get-book?id=1接口如下:
奇怪的是在线生成解决方案的时分,UI结构挑选了MVC,可是仍是呈现了这个项目,而且在发动Acme.BookStore.Web项目的时分,如果不发动Acme.BookStore.HttpApi.Host项目,还会报错Volo.Abp.AbpException: Remote service ‘AbpMvcClient’ was not found and there is no default configuration,而且还没有找到Acme.BookStore.Web项目在哪里用到了Acme.BookStore.HttpApi.Host项目。由于自己首要关注前后端别离的项目,所以就不纠结这个细节了。
在线生成解决方案中还包括:Acme.BookStore.IdentityServer(认证授权项目),Acme.BookStore.HttpApi.Client(长途服务代理层),Acme.BookStore.Web(前后端不别离的展示层),Acme.BookStore.TestBase(其它项目同享或运用的类),Acme.BookStore.Domain.Tests(测验范畴层目标),Acme.BookStore.EntityFrameworkCore.Tests(测验自界说仓储完成或EF Core映射),Acme.BookStore.Application.Tests(测验运用层目标),Acme.BookStore.HttpApi.Client.ConsoleTestApp(从.NET操控台中调用HTTP API)等。一篇文章放不下,后面继续解说实践。
参阅文献:
[1]聊一聊ABP vNext的模块化体系:www.sohu.com/a/436373…
[2]Abp vNext源码分析文章目录:www.cnblogs.com/myzon…
[3]手动从0建立ABP结构-ABP官方完好解决方案源码:url39.ctfile.com/f/25… (拜访暗码: 2096)
[4]手动从0建立ABP结构-手动建立简化解决方案源码:url39.ctfile.com/f/25… (拜访暗码: 2096)
本文由mdnice多平台发布
来源: 手动从0建立ABP结构-ABP官方完好解决方案和手动建立简化解决方案实践