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]//授权
posted @   北落师门、  阅读(173)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示