发现ABP——完成CRUD
看了整整周末两天的abp文档,使用项目模板写了一个简单的小例子,心得满满。
首先先看一下abp的模板项目结构(ProjectTracker是csla.net框架的事例项目名称):
生成模板就不细说,直接在官方生成或使用CLI命令,可以生成我们想要的命名空间的项目。
拿到生成的项目模板,改一下ProjectTracker.DbMigrator中的连接字符串为自己的数据库地址,以此项目作为启动项目F5,系统表就建好了,后面就是通过EFCore来更新表结构。
下面说一下自己在写一个ProjectInfo项目基本信息CRUD的各层实现(最简单的单表操作)
- 创建实体对象,在这里也是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 }
基类实现主键的定义,需要显示声明数据类型,按照上述实体可以生成以下的数据表信息:
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 );
可以看到多了不少辅助信息,其中还有一个ExtraProperties,大体猜测应该是可以存储动态扩展数据。
以上实体的定义是在ProjectTracker.Domain项目中,这也是abp框架约定好的。 - 创建实体相关的数据,包含属性的枚举类型、相关常量信息等,作为公共数据被引用,项目在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 }
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 }
- 实体声明完,现在将实体对象添加到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 }
然后添加字段的信息,在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 }
可以通过执行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 }
在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 }
仓储接口和实现中中无用例方法?其实是在基类仓储中已经实现了常用的一些方法,足以在应用层中使用,所以~~但是~~最终我们还是正经来学!
- 以上是定义实体和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 }
用例呢?这里面的还是硬写上去的,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 }
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 }
实现对象映射(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 }
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 }
继承了强大的父类,以及仅一个自定义的用例方法,这里面我们实现开发中肯定会写用例方法,还是定义好接口。
- 以上是我们从实体对象到生成数据表结构,从定义和实现仓储,从定义和实现服务以及定义传输对象Dto的过程,生成一下项目,然后准备起飞~~~~
ProjectTracker.HttpApi.Host是web api项目,通过上面实现的AppService服务,abp可以帮我们自动实现webapi,以前我们写的Controller在这里已经不用手写了,abp会根据服务方法名自动识别Get、Post等http方法,并将参数处理好(所以服务中的方法要按abp的约定来搞)。
我们运行Host项目:
找一下我们写的ProjectInfo:
实现到这里我已经被惊艳到了,REST API就这么被实现了,迫切的插入几条数据试一下好不好用:
POST和GET均成功!
- 以上是实现的过程,回头再说文档,ABP中为我们约定好了相当多的东西,通过官方的推荐我们可以快速实现一些想要的功能,当然上述代码只是最简单的,文档中使用了大量的“推荐”如下图
大量的“推荐”语告诉我们如何以最佳的方式实现代码编写,而且abp的更新速度还是挺快的,文档和源码是我们获取帮助的最快和最好的方式。
文中代码非项目代码,没有严谨性(如多个少个属性的情况),只供学习和了解使用abp搭建项目的结构。
后面会通过“平时写的代码如何在ABP中实现”的思路来学习,比如如何实现依赖注入,如何使用rabbitmq,如何事务处理等。
好了,希望对大家有所帮助,美好的一周即将开始,祝大家工作、学习愉快~~~~
From:发现ABP——完成CRUD - 屈鲁奇 - 博客园 (cnblogs.com)
作者:屈鲁奇
出处:https://www.cnblogs.com/quluqi
联系:496195435@qq.com QQ:496195435