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]//授权