ABP使用学习纯后端(二)
ABP使用学习
创建项目(纯后端项目)
abp new 项目名称 -u none (不包含前端) -v 6.0.0(版本号)
连接数据库
- 修改Acme.BookStore.DbMigrator中的appsettings.json中的连接字符串
- Acme.BookStore.HttpApi.Host中的appsettings.json中的连接字符串
创建表并且迁移到数据库
-
在Domain层中创建实体
-
一个实体对应一个文件夹(以Book为例)
-
//需要继承 AuditedAggregateRoot<T>这个类(为审计字段) T为主键的类型,主键默认命名为Id public class Book:AuditedAggregateRoot<Guid> { public string BName { get; set; } public BookType BType { get; set; } public DateTime BPublishDate { get; set; } public float BPrice { get; set; } }
-
-
在Domain.Shared层创建枚举之类的
-
在EntityFrameworkCore层的BookStoreDbContext的上下文创建实体
-
public class BookStoreDbContext : AbpDbContext<BookStoreDbContext>, IIdentityDbContext, ITenantManagementDbContext { public DbSet<Book> Books { get; set; }//创建Book表 }
-
-
在迁移时加入数据库约束和数据
-
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.ConfigureIdentity(); builder.ConfigureOpenIddict(); builder.ConfigureFeatureManagement(); builder.ConfigureTenantManagement(); /* Configure your own tables/entities inside here */ //跟以前的写法一样 builder.Entity<Book>(b => { b.ToTable(BookStoreConsts.DbTablePrefix + nameof(Book), BookStoreConsts.DbSchema); b.ConfigureByConvention(); //auto configure for the base class props //... b.Property(c => c.BName).IsRequired().HasMaxLength(50); }); }
-
-
迁移数据库,将程序包管理器的默认项目设置为EntityFrameworkCore
- 正常使用数据库迁移指令即可
创建服务和生成动态的API(自动生成)
-
在Application.Contracts层中创建一个类的文件夹
-
需要定义一个实体的显示DTO和实体的添加修改DTO,服务接口
-
//需要继承 AuditedEntityDto<T>带审计字段DTO基类 T为主键的类型这个是用于显示的 public class BookDTO:AuditedEntityDto<Guid> { public string BName { get; set; } public BookType BType { get; set; } public DateTime BPublishDate { get; set; } public float BPrice { get; set; } } -
//以这个表为添加和修改的DTO并且可以直接搁这里面加入约束,swagger中会根据该约束进行判断 public class CreateUpdateBookDto { [Required] [StringLength(128)] public string BName { get; set; } [Required] public BookType BType { get; set; } = BookType.Undefined; [Required] [DataType(DataType.Date)] public DateTime BPublishDate { get; set; } = DateTime.Now; [Required] public float BPrice { get; set; } }
-
-
创建实体的服务接口
-
public interface IBookAppService : ICrudAppService< //Defines CRUD methods(创建CRUD方法,CRUD为增删改查) BookDTO, //Used to show books(数据显示的类型(直接用显示的DTO)) Guid, //Primary key of the book entity(主键,删除和修改根据主键进行) PagedAndSortedResultRequestDto, //Used for paging/sorting(分页使用的表(DTO)) CreateUpdateBookDto> //Used to create/update a book(添加修改使用的DTO) { }
-
-
创建实体的服务
-
//需要继承 CrudAppServic接口<实体,显示的类型,主键,分页,添加修改>(创建一个定义了Crud的服务实现),定义的接口 public class BookAppService : CrudAppService<Book, BookDTO, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>, IBookAppService { public BookAppService(IRepository<Book, Guid> repository) : base(repository) { } }
-
创建自定义服务和仓储
-
在Application.Contracts层中创建一个类的文件夹
-
需要定义一个实体的显示DTO和实体的添加修改DTO,并且包含实体的服务接口
-
//需要继承IApplicationService(实现这个接口的类型应该被当作一个应用服务来对待) public interface IAuthorService:IApplicationService { Task<AuthorDTO> GetAsync(Guid id); }
-
-
BookStore.Domain层的实体文件夹定义实体的仓储接口
-
//用于定义对数据库中的数据实体进行操作的接口 public interface IAuthorRepository:IRepository<Author,Guid> { Task<Author> GetAsync(Guid id); }
-
-
EntityFrameworkCore层的实体文件夹中定义实体的仓储实现
-
public class EFCoreAuthorRepository : EfCoreRepository<BookStoreDbContext, Author, Guid>, //EfCoreRepository(或类似的名称)是 IRepository 接口的一个具体实现,它使用EF Core作为ORM(对象关系映射器)来与数据库交互。 IAuthorRepository//继承的接口 { //IDbContextProvider提供对 DbContext 的访问 public EFCoreAuthorRepository(IDbContextProvider<BookStoreDbContext> dbContextProvider) : base(dbContextProvider) { } public async Task<Author> GetAsync(Guid id) { var dbSet=await GetDbSetAsync(); return await dbSet.FirstOrDefaultAsync(c => c.Id == id); } }
-
-
定义服务实现
-
public class AuthorService : BookStoreAppService, IAuthorService { private readonly IAuthorRepository repository; private readonly IMapper mapper; public AuthorService(IAuthorRepository repository, IMapper mapper) { this.repository = repository; this.mapper = mapper; } public async Task<AuthorDTO> GetAsync(Guid id) { var entity=repository.GetAsync(id); return mapper.Map<AuthorDTO>(entity); } }
结合使用
不用定义仓储
-
定义服务的接口
-
//需要继承IApplicationService(实现这个接口的类型应该被当作一个应用服务来对待) public interface IAuthorService:IApplicationService { Task<AuthorDTO> GetAsync(Guid id); }
-
-
定义服务的实现
-
可以直接使用IRepository实现仓储的定义
-
public class AuthorService : BookStoreAppService, IAuthorService { private readonly IRepository<Admin,Guid> repository; private readonly IMapper mapper; public AuthorService(IRepository<Admin,Guid> repository, IMapper mapper) { this.repository = repository; this.mapper = mapper; } public async Task<AuthorDTO> GetAsync(Guid id) { var entity=repository.GetAsync(id); return mapper.Map<AuthorDTO>(entity); } } -
使用语法糖
-
//需要继承 CrudAppServic接口<实体,显示的类型,主键,分页,添加修改>(创建一个定义了Crud的服务实现),定义的接口 public class BookAppService : CrudAppService<Book, BookDTO, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>, IBookAppService { public BookAppService(IRepository<Book, Guid> repository) : base(repository) { } //使用重写直接重写需要用的方法 public override async Task<BookDTO> CreateAsync(CreateUpdateBookDto input) { return base.CreateAsync(input); } }
-
-
继承的区别
AuditedEntityDto和EntityDto的区别
- EntityDto:这是基于实体的DTO,用于在不同的层之间传递实体的基本信息。它可能只包含实体的标识符(ID)和一些基本属性,不包含审计信息或完整的业务逻辑。(不包含审计字段)
- AuditedEntityDto:这个DTO继承了CreationAuditedEntityDto,并在此基础上封装了额外的审计信息,如最后修改时间(
LastModificationTime
)和最后修改者用户ID(LastModifierUserId
)。这些信息对于需要跟踪数据更改历史的场景非常有用。(包含审计字段)
CrudAppService
作用:自动创建一个实现Crud的服务实现,仓储接口同理
public class BookAppService : CrudAppService<Book, BookDTO, Guid, PagedAndSortedResultRequestDto, CreateUpdateBookDto>
ICrudAppService
作用:自动创建一个实现Crud的服务接口,可以不带实体,因为使用了范式,但是需要定义显示DTO,主键和分页的DTO还有添加修改的DTO
IRepository
作用:用于定义对数据库中的数据实体进行操作的接口
EfCoreRepository
EfCoreRepository
(或类似的名称)是IRepository
接口的一个具体实现,它使用EF Core作为ORM(对象关系映射器)来与数据库交互。- 这个实现类会包含EF Core的
DbContext
的引用,并通过它来执行数据库操作。 - 它会将接口中的方法转换为具体的EF Core查询和命令。
如何将ABP改成自己需要的模式
注册服务和使用服务:通过ABP中的HOST的层封装好的HttpApiHostModule.cs文件中进行服务的注册
public override void ConfigureServices(ServiceConfigurationContext context) { var configuration = context.Services.GetConfiguration(); var hostingEnvironment = context.Services.GetHostingEnvironment(); //注册服务 YitIdHelper.SetIdGenerator(new IdGeneratorOptions()); //注册上下文:AOP里面可以获取IOC对象,如果有现成框架比如Furion可以不写这一行 //context等于以前的builder context.Services.AddHttpContextAccessor(); context.Services.AddMemoryCache(); //验证码 ConfigureCaptcha(context, configuration); //允许使用Post ConfigureAntiForgery(); //配置JWT(ABP自带的JWT授权,需要我们自己修改成自己想要的) ConfigureAuthentication(context); //自带 ConfigureBundles(); ConfigureUrls(configuration); ConfigureConventionalControllers(); ConfigureLocalization(); ConfigureVirtualFileSystem(context); ConfigureCors(context, configuration); //配置swagger(ABP自带,可以直接修改成我们想要的) ConfigureSwaggerServices(context, configuration); } //例如 /// <summary> /// 验证码配置 /// </summary> /// <param name="context"></param> /// <param name="configuration"></param> private void ConfigureCaptcha(ServiceConfigurationContext context, IConfiguration configuration) { // 默认使用内存存储(AddDistributedMemoryCache) context.Services.AddCaptcha(configuration); // 全部配置 context.Services.AddCaptcha(configuration, option => { option.CaptchaType = CaptchaType.WORD; // 验证码类型 option.CodeLength = 6; // 验证码长度, 要放在CaptchaType设置后. 当类型为算术表达式时,长度代表操作的个数 option.ExpirySeconds = 30; // 验证码过期时间 option.IgnoreCase = true; // 比较时是否忽略大小写 option.StoreageKeyPrefix = ""; // 存储键前缀 option.ImageOption.Animation = true; // 是否启用动画 option.ImageOption.FrameDelay = 30; // 每帧延迟,Animation=true时有效, 默认30 option.ImageOption.Width = 150; // 验证码宽度 option.ImageOption.Height = 50; // 验证码高度 option.ImageOption.BackgroundColor = SkiaSharp.SKColors.White; // 验证码背景色 option.ImageOption.BubbleCount = 2; // 气泡数量 option.ImageOption.BubbleMinRadius = 5; // 气泡最小半径 option.ImageOption.BubbleMaxRadius = 15; // 气泡最大半径 option.ImageOption.BubbleThickness = 1; // 气泡边沿厚度 option.ImageOption.InterferenceLineCount = 2; // 干扰线数量 option.ImageOption.FontSize = 36; // 字体大小 option.ImageOption.FontFamily = DefaultFontFamilys.Instance.Actionj; // 字体 /* * 中文使用kaiti,其他字符可根据喜好设置(可能部分转字符会出现绘制不出的情况)。 * 当验证码类型为“ARITHMETIC”时,不要使用“Ransom”字体。(运算符和等号绘制不出来) */ option.ImageOption.TextBold = true;// 粗体,该配置2.0.3新增 }); } /// <summary> /// 一起Program中的App /// </summary> /// <param name="context"></param> public override void OnApplicationInitialization(ApplicationInitializationContext context) { var app = context.GetApplicationBuilder(); var env = context.GetEnvironment(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseAbpRequestLocalization(); if (!env.IsDevelopment()) { app.UseErrorPage(); } app.UseCorrelationId(); app.UseStaticFiles(); app.UseRouting(); app.UseCors(); app.UseAuthentication(); //app.UseAbpOpenIddictValidation(); //if (MultiTenancyConsts.IsEnabled) //{ // app.UseMultiTenancy(); //} app.UseUnitOfWork(); app.UseAuthorization(); app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "基础接口"); c.SwaggerEndpoint("/swagger/v2/swagger.json", "业务接口"); // 模型的默认扩展深度,设置为 -1 完全隐藏模型 c.DefaultModelsExpandDepth(0); // API文档仅展开标记 c.DocExpansion(DocExpansion.List); // API前缀设置为空 c.RoutePrefix = string.Empty; // API页面Title c.DocumentTitle = "😍接口文档 - 农业产品小组"; }); app.UseAuditing(); app.UseAbpSerilogEnrichers(); app.UseConfiguredEndpoints(); }
因为定义了swagger的分类和授权所以需要我们的接口加上授权验证和接口分类
[ApiExplorerSettings(GroupName = "v1")]//方法分类 [Authorize]//授权