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 @ 2024-06-26 15:56  北落师门、  阅读(109)  评论(0编辑  收藏  举报