发现ABP——完成CRUD

  看了整整周末两天的abp文档,使用项目模板写了一个简单的小例子,心得满满。

  首先先看一下abp的模板项目结构(ProjectTracker是csla.net框架的事例项目名称):

生成模板就不细说,直接在官方生成或使用CLI命令,可以生成我们想要的命名空间的项目。
拿到生成的项目模板,改一下ProjectTracker.DbMigrator中的连接字符串为自己的数据库地址,以此项目作为启动项目F5,系统表就建好了,后面就是通过EFCore来更新表结构。

下面说一下自己在写一个ProjectInfo项目基本信息CRUD的各层实现(最简单的单表操作)

  1. 创建实体对象,在这里也是DDD中的聚合根对象,abp已经为我们封闭好了实体基类,实现了不同组合的审计属性,本例中使用最全的FullAuditedAggregateRoot,实现可以F12看源码看实现,代码如下:
     1 using JetBrains.Annotations;
     2 using System;
     3 using System.Collections.Generic;
     4 using System.Text;
     5 using Volo.Abp.Domain.Entities.Auditing;
     6 
     7 namespace ProjectTracker.Project
     8 {
     9     public class ProjectInfo : FullAuditedAggregateRoot<Guid>
    10     {
    11         /// <summary>
    12         /// 项目名称
    13         /// </summary>
    14         public string ProjectFullName { get; private set; }
    15         /// <summary>
    16         /// 项目简称
    17         /// </summary>
    18         public string ProjectSortName { get; private set; }
    19         /// <summary>
    20         /// 项目编号
    21         /// </summary>
    22         public string ProjectCode { get; set; }
    23         /// <summary>
    24         /// 立项时间
    25         /// </summary>
    26         public DateTime ApprovalDate { get; private set; }
    27         /// <summary>
    28         /// 合同签订时间
    29         /// </summary>
    30         public DateTime? ContractDate { get; private set; }
    31         /// <summary>
    32         /// 项目类型
    33         /// </summary>
    34         public ProjectType ProjectType { get; private set; }
    35 
    36         protected ProjectInfo()
    37         { }
    38 
    39         public ProjectInfo(Guid id, [NotNull] string projectFullName, string projectSortName,
    40             [NotNull] DateTime approvalDate, DateTime? contractDate,
    41             [NotNull] ProjectType projectType, [NotNull] string projectCode)
    42         {
    43             this.Id = id;
    44             ProjectFullName = projectFullName;
    45             ProjectSortName = projectSortName;
    46             ApprovalDate = approvalDate;
    47             ContractDate = contractDate;
    48             ProjectType = projectType;
    49             ProjectCode = projectCode;
    50         }
    51 
    52         public void SetProjectFullName([NotNull] string projectFullName)
    53         {
    54             ProjectFullName = Volo.Abp.Check.NotNullOrWhiteSpace(projectFullName, nameof(projectFullName), 500);
    55         }
    56     }
    57 }
    View Code

    基类实现主键的定义,需要显示声明数据类型,按照上述实体可以生成以下的数据表信息:

     1 CREATE TABLE [dbo].[pm_ProjectInfo] (
     2     [Id]                   UNIQUEIDENTIFIER NOT NULL,
     3     [ExtraProperties]      NVARCHAR (MAX)   NULL,
     4     [ConcurrencyStamp]     NVARCHAR (40)    NULL,
     5     [CreationTime]         DATETIME2 (7)    NOT NULL,
     6     [CreatorId]            UNIQUEIDENTIFIER NULL,
     7     [LastModificationTime] DATETIME2 (7)    NULL,
     8     [LastModifierId]       UNIQUEIDENTIFIER NULL,
     9     [IsDeleted]            BIT              DEFAULT (CONVERT([bit],(0))) NOT NULL,
    10     [DeleterId]            UNIQUEIDENTIFIER NULL,
    11     [DeletionTime]         DATETIME2 (7)    NULL,
    12     [ProjectFullName]      NVARCHAR (500)   NOT NULL,
    13     [ProjectSortName]      NVARCHAR (500)   NULL,
    14     [ApprovalDate]         DATETIME2 (7)    NOT NULL,
    15     [ContractDate]         DATETIME2 (7)    NULL,
    16     [ProjectType]          INT              NOT NULL,
    17     [ProjectCode]          NVARCHAR (50)    DEFAULT (N'') NOT NULL,
    18     CONSTRAINT [PK_pm_ProjectInfo] PRIMARY KEY CLUSTERED ([Id] ASC)
    19 );
    View Code

    可以看到多了不少辅助信息,其中还有一个ExtraProperties,大体猜测应该是可以存储动态扩展数据。

    以上实体的定义是在ProjectTracker.Domain项目中,这也是abp框架约定好的。
  2. 创建实体相关的数据,包含属性的枚举类型、相关常量信息等,作为公共数据被引用,项目在ProjectTracker.Domain.Share,在此以创建一个项目类型为例:
     1 namespace ProjectTracker.Project
     2 {
     3     /// <summary>
     4     /// 项目类型
     5     /// </summary>
     6     public enum ProjectType
     7     {
     8         /// <summary>
     9         /// 设计咨询类
    10         /// </summary>
    11         DesignAndConsulting = 1,
    12         /// <summary>
    13         /// 监理类
    14         /// </summary>
    15         Supervision = 2,
    16         /// <summary>
    17         /// 工程类
    18         /// </summary>
    19         Engineering = 3
    20     }
    21 }
    View Code
    创建数据库对应的DbContext时还会用到各属性长度等常量 ,也在此处给予声明:
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Text;
     4 
     5 namespace ProjectTracker.Project
     6 {
     7     public static class ProjectInfoConsts
     8     {
     9         public const string TableName = "ProjectInfo";
    10         public const int ProjectFullNameMaxLength = 500;
    11         public const int ProjectSortNameMaxLength = 500;
    12         public const int ProjectCodeMaxLength = 50;
    13     }
    14 }
    View Code
  3. 实体声明完,现在将实体对象添加到DbContext中,项目为ProjectTracker.EntityFrameworkCore,操作与普通的EFCore一样,ProjectTrackerDbContext.cs代码如下,仅添加了一行DbSet:
     1 using Microsoft.EntityFrameworkCore;
     2 using ProjectTracker.Project;
     3 using ProjectTracker.Users;
     4 using Volo.Abp.Data;
     5 using Volo.Abp.EntityFrameworkCore;
     6 using Volo.Abp.EntityFrameworkCore.Modeling;
     7 using Volo.Abp.Identity;
     8 using Volo.Abp.Users.EntityFrameworkCore;
     9 
    10 namespace ProjectTracker.EntityFrameworkCore
    11 {
    12     /* This is your actual DbContext used on runtime.
    13      * It includes only your entities.
    14      * It does not include entities of the used modules, because each module has already
    15      * its own DbContext class. If you want to share some database tables with the used modules,
    16      * just create a structure like done for AppUser.
    17      *
    18      * Don't use this DbContext for database migrations since it does not contain tables of the
    19      * used modules (as explained above). See ProjectTrackerMigrationsDbContext for migrations.
    20      */
    21     [ConnectionStringName("Default")]
    22     public class ProjectTrackerDbContext : AbpDbContext<ProjectTrackerDbContext>
    23     {
    24         public DbSet<AppUser> Users { get; set; }
    25 
    26         /* Add DbSet properties for your Aggregate Roots / Entities here.
    27          * Also map them inside ProjectTrackerDbContextModelCreatingExtensions.ConfigureProjectTracker
    28          */
    29 
    30         public DbSet<ProjectInfo> ProjectInfos { get; set; }
    31 
    32         public ProjectTrackerDbContext(DbContextOptions<ProjectTrackerDbContext> options)
    33             : base(options)
    34         {
    35 
    36         }
    37 
    38         protected override void OnModelCreating(ModelBuilder builder)
    39         {
    40             base.OnModelCreating(builder);
    41 
    42             /* Configure the shared tables (with included modules) here */
    43 
    44             builder.Entity<AppUser>(b =>
    45             {
    46                 b.ToTable(AbpIdentityDbProperties.DbTablePrefix + "Users"); //Sharing the same table "AbpUsers" with the IdentityUser
    47 
    48                 b.ConfigureByConvention();
    49                 b.ConfigureAbpUser();
    50 
    51                 /* Configure mappings for your additional properties
    52                  * Also see the ProjectTrackerEfCoreEntityExtensionMappings class
    53                  */
    54             });
    55 
    56             /* Configure your own tables/entities inside the ConfigureProjectTracker method */
    57 
    58             builder.ConfigureProjectTracker();
    59         }
    60     }
    61 }
    View Code

    然后添加字段的信息,在ProjectTrackerDbContextModelCreatingExtensions.cs中:

     1 using Microsoft.EntityFrameworkCore;
     2 using ProjectTracker.Project;
     3 using Volo.Abp;
     4 using Volo.Abp.EntityFrameworkCore.Modeling;
     5 
     6 namespace ProjectTracker.EntityFrameworkCore
     7 {
     8     public static class ProjectTrackerDbContextModelCreatingExtensions
     9     {
    10         public static void ConfigureProjectTracker(this ModelBuilder builder)
    11         {
    12             Check.NotNull(builder, nameof(builder));
    13 
    14             builder.Entity<Project.ProjectInfo>(b =>
    15             {
    16                 b.ToTable(ProjectTrackerConsts.DbTablePrefix + ProjectInfoConsts.TableName, ProjectTrackerConsts.DbSchema);
    17                 b.ConfigureByConvention();
    18                 b.Property(c => c.ProjectFullName).IsRequired().HasMaxLength(ProjectInfoConsts.ProjectFullNameMaxLength);
    19                 b.Property(c => c.ProjectSortName).HasMaxLength(ProjectInfoConsts.ProjectSortNameMaxLength);
    20                 b.Property(c => c.ProjectCode).IsRequired().HasMaxLength(ProjectInfoConsts.ProjectCodeMaxLength);
    21                 b.Property(c => c.ProjectType).IsRequired();
    22             });
    23         }
    24     }
    25 }
    View Code

    可以通过执行add-migration和update-database命令来将数据表结构更新


    Domain层还要定义一下仓储的接口,如下:
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Text;
     4 using Volo.Abp.Domain.Repositories;
     5 
     6 namespace ProjectTracker.Project
     7 {
     8     public interface IProjectInfoRepository : IRepository<ProjectInfo, Guid>
     9     { }
    10 }
    View Code

    在ProjectTracker.EntityFrameworkCore层中实现仓储接口,如下:

     1 using ProjectTracker.EntityFrameworkCore;
     2 using System;
     3 using System.Collections.Generic;
     4 using System.Text;
     5 using Volo.Abp.Domain.Repositories;
     6 using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
     7 using Volo.Abp.EntityFrameworkCore;
     8 
     9 namespace ProjectTracker.Project
    10 {
    11     public class ProjectInfoRepository :
    12         EfCoreRepository<ProjectTrackerDbContext, Project.ProjectInfo, Guid>,
    13         IProjectInfoRepository
    14     {
    15         public ProjectInfoRepository(IDbContextProvider<ProjectTrackerDbContext> dbContext)
    16             : base(dbContext)
    17         {
    18 
    19         }
    20     }
    21 }
    View Code

    仓储接口和实现中中无用例方法?其实是在基类仓储中已经实现了常用的一些方法,足以在应用层中使用,所以~~但是~~最终我们还是正经来学!

     

  4. 以上是定义实体和EF Context配置的过程,下面我们看Application层的实现
    首先定义服务接口IProjectInfoAppService.cs,服务接口以及Dto对象均创建在ProjectTracker.Application.Contracts层中,对象不会暴露具体实现层:
    IProjectInfoAppService.cs:
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Text;
     4 using System.Threading.Tasks;
     5 
     6 namespace ProjectTracker.Project
     7 {
     8     public interface IProjectInfoAppService : Volo.Abp.Application.Services.ICrudAppService<
     9             ProjectDto,
    10             Guid,
    11             Volo.Abp.Application.Dtos.PagedAndSortedResultRequestDto,
    12             CreateUpdateProjectDto,
    13             CreateUpdateProjectDto>
    14     {
    15         Task<ProjectDto> GetProjectDtoByCode(string projectCode);
    16     }
    17 }
    View Code

    用例呢?这里面的还是硬写上去的,abp已经帮我们封装好了CRUD的标准操作,省心。。。

    定义数据传输对象ProjectDto.cs和CreateUpdateProjectDto.cs的实现:
     1 using System;
     2 using System.ComponentModel.DataAnnotations;
     3 
     4 namespace ProjectTracker.Project
     5 {
     6     [Serializable]
     7     public class ProjectDto : Volo.Abp.Application.Dtos.ExtensibleEntityDto<Guid>
     8     {
     9         [Display(Name = "项目名称")]
    10         public string ProjectFullName { get; set; }
    11 
    12         [Display(Name = "项目简称")]
    13         public string ProjectSortName { get; set; }
    14 
    15         [MaxLength(50)]
    16         [Display(Name = "项目编号")]
    17         public string ProjectCode { get; set; }
    18 
    19         [Display(Name = "立项时间")]
    20         public DateTime ApprovalDate { get; set; }
    21 
    22         [Display(Name = "合同签订时间")]
    23         public DateTime? ContractDate { get; set; }
    24 
    25         [Display(Name = "项目类型")]
    26         public string ProjectType { get; set; }
    27     }
    28 }
    View Code
     1 using System;
     2 using System.ComponentModel.DataAnnotations;
     3 
     4 namespace ProjectTracker.Project
     5 {
     6     [Serializable]
     7     public class CreateUpdateProjectDto : Volo.Abp.Application.Dtos.ExtensibleEntityDto<Guid>
     8     {
     9         [Required]
    10         [MaxLength(500)]
    11         [Display(Name = "项目名称")]
    12         public string ProjectFullName { get; set; }
    13 
    14         [MaxLength(500)]
    15         [Display(Name = "项目简称")]
    16         public string ProjectSortName { get; set; }
    17 
    18         [MaxLength(50)]
    19         [Display(Name = "项目编号")]
    20         public string ProjectCode { get; set; }
    21 
    22         [Display(Name = "立项时间")]
    23         public DateTime ApprovalDate { get; set; }
    24 
    25         [Display(Name = "合同签订时间")]
    26         public DateTime? ContractDate { get; set; }
    27 
    28         [EnumDataType(typeof(ProjectType))]
    29         [Display(Name = "项目类型")]
    30         public ProjectType ProjectType { get; set; }
    31     }
    32 }
    View Code


    这里只是大概实现,同样是强大的基类,这里添加了一些数据校验,在前端传输到服务端时进行第一层的校验。

    实现对象映射(abp是使用AutoMapper),在ProjectTrackerApplicationAutoMapperProfile.cs类中已经定义好了位置,我们写映射即可:
     1 using AutoMapper;
     2 using ProjectTracker.Project;
     3 
     4 namespace ProjectTracker
     5 {
     6     public class ProjectTrackerApplicationAutoMapperProfile : Profile
     7     {
     8         public ProjectTrackerApplicationAutoMapperProfile()
     9         {
    10             CreateMap<ProjectInfo, ProjectDto>()
    11                 .ForMember(t => t.ProjectType,
    12                 memberOptions => memberOptions.MapFrom(j => j.ProjectType.ToString()));
    13             CreateMap<CreateUpdateProjectDto, ProjectInfo>();
    14         }
    15     }
    16 }
    View Code

     

    创建应用服务ProjectInfoAppService.cs,它也相当于用例实现,在此会接收和返回Dto对象,调用仓储实现数据操作,代码如下:
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Text;
     4 using System.Threading.Tasks;
     5 using Volo.Abp.Application.Dtos;
     6 using Volo.Abp.Application.Services;
     7 using Volo.Abp.Domain.Repositories;
     8 
     9 namespace ProjectTracker.Project
    10 {
    11     public class ProjectInfoAppService : CrudAppService<
    12         ProjectInfo,
    13         ProjectDto,
    14         Guid,
    15         PagedAndSortedResultRequestDto,
    16         CreateUpdateProjectDto,
    17         CreateUpdateProjectDto>, IProjectInfoAppService
    18     {
    19         private readonly IProjectInfoRepository _repository;
    20 
    21         public ProjectInfoAppService(IProjectInfoRepository repository)
    22             : base(repository)
    23         {
    24             this._repository = repository;
    25         }
    26 
    27         public async Task<ProjectDto> GetProjectDtoByCode(string projectCode)
    28         {
    29             var entity = await _repository.GetAsync(t => t.ProjectSortName == projectCode);
    30             var data = base.ObjectMapper.Map<ProjectInfo, ProjectDto>(entity);
    31             return data;
    32         }
    33     }
    34 }
    View Code

    继承了强大的父类,以及仅一个自定义的用例方法,这里面我们实现开发中肯定会写用例方法,还是定义好接口。

  5. 以上是我们从实体对象到生成数据表结构,从定义和实现仓储,从定义和实现服务以及定义传输对象Dto的过程,生成一下项目,然后准备起飞~~~~

    ProjectTracker.HttpApi.Host是web api项目,通过上面实现的AppService服务,abp可以帮我们自动实现webapi,以前我们写的Controller在这里已经不用手写了,abp会根据服务方法名自动识别Get、Post等http方法,并将参数处理好(所以服务中的方法要按abp的约定来搞)。
    我们运行Host项目:

     

    找一下我们写的ProjectInfo:

     

     实现到这里我已经被惊艳到了,REST API就这么被实现了,迫切的插入几条数据试一下好不好用:

     

    POST和GET均成功!

  6. 以上是实现的过程,回头再说文档,ABP中为我们约定好了相当多的东西,通过官方的推荐我们可以快速实现一些想要的功能,当然上述代码只是最简单的,文档中使用了大量的“推荐”如下图

     

    大量的“推荐”语告诉我们如何以最佳的方式实现代码编写,而且abp的更新速度还是挺快的,文档和源码是我们获取帮助的最快和最好的方式。
    文中代码非项目代码,没有严谨性(如多个少个属性的情况),只供学习和了解使用abp搭建项目的结构。

    后面会通过“平时写的代码如何在ABP中实现”的思路来学习,比如如何实现依赖注入,如何使用rabbitmq,如何事务处理等。
    好了,希望对大家有所帮助,美好的一周即将开始,祝大家工作、学习愉快~~~~

    From:发现ABP——完成CRUD - 屈鲁奇 - 博客园 (cnblogs.com)

 

posted @ 2020-07-19 22:18  屈鲁奇  阅读(841)  评论(1编辑  收藏  举报