ABP框架之CRUD简单示例
最近在学习ABP框架,打算通过一个简单的增删改查示例来快速入门,厘清整个框架的层次逻辑。
1. 前期准备
1.1 创建项目
进入这里下载ABP启动模板:
选择 Multi Page Web Application

创建项目

解压下载好的压缩包,使用 visual studio2022 (其他版本可能有问题)打开解决方案(即College.sln文件)

1.2 使用 MySQL 数据库
ABP 项目初始化后默认使用的是 SQL Server 数据库,要使用 MySQl 数据库需要进行一些配置。
1.2.1 移除并安装相关套件
- 在College.EntityFrameworkCore下移除 xxxx.SqlServer
  
- 在College.EntityFrameworkCore下移除 Microsoft.EntityFrameworkCore.Design
  
- 在College.EntityFrameworkCore下安装 Pomelo.EntityFrameworkCore.MySql
  
- 在College.EntityFrameworkCore下安装 Pomelo.EntityFrameworkCore.MySql.Design
  
- 在College.Web.Host下移除 Microsoft.EntityFrameworkCore.Design
  
- 在College.Web.Host下安装 Microsoft.EntityFrameworkCore.Tools
  
1.2.2 配置 MySQL 数据库的连接
- 按下面三张截图修改数据库连接字符串
  
  
  
- 修改College.EntityFrameworkCore下的EntityFrameworkCore文件夹下的CollegeDbContextConfigurer.cs
  
1.2.3 初始化数据库
- 删除College.EntityFrameworkCore下的Migrations文件夹
  
- 设置 College.Web.Host 为启动项
  
- 打开程序包管理控制台,选择EntityFrameworkCore
  
- 依次输入下列命令
Add-Migration "AbpZero_Initial"
Update-Database "AbpZero_Initial"
出现下图的信息则意味成功:

打开数据库,可以发现数据库已经创建

1.2.4 迁移数据库
- 设置College.Migrator为启动项
  
- 运行,输入Y,回车
  
- 大功告成
  
1.3 启动项目
- 设置College.Web.Host为启动项目
- 运行,出现下图情况则说明配置成功
  
- 测试一下API
 授权,账号为admin,密码是123qwe
  
  
  
 返回状态码200说明请求成功
  
- 设置College.Web.Mvc为启动项目
- 运行,出现下图界面说明整个项目都没有问题
  
- 发现样式不对,是因为缺少libs,按照下图操作,再重新启动就好了
  
- 登录,界面显示如下图,到这里准备工作就完成了
  
  
2. 领域层创建实体
在领域层(即College.Core)下创建文件夹 Entities, 用于存放实体
在Entities下创建实体类 Course.cs 用于创建课程信息
using Abp.Domain.Entities;
using Abp.Domain.Entities.Auditing;
using Abp.Timing;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace College.Entities
{
    public class Course : Entity<int>, IHasCreationTime
    {
        public Course ()
        {
            this.Code = string.Empty;
            this.DepartmentCode = string.Empty;
            this.Name = string.Empty;
            this.Credits = 0;
            this.Description = string.Empty;
            this.Status = 0;
            this.CreateDate = null;
            this.CreateName = string.Empty;
            this.UpdateDate = null;
            this.UpdateName = string.Empty;
            this.CreationTime = Clock.Now;
        }
        /// <summary>
        /// 课程编号
        /// </summary>
        [StringLength(50)]
        public string Code { get; set; }
        /// <summary>
        /// 院系编号
        /// </summary>
        [StringLength(50)]
        public string DepartmentCode { get; set; }
        /// <summary>
        /// 课程名称
        /// </summary>
        [StringLength (150)]
        public string Name { get; set; }
        /// <summary>
        /// 课程积分
        /// </summary>
        [Range(0, 5)]
        public int Credits { get; set; }
        /// <summary>
        /// 备注
        /// </summary>
        [StringLength(200)]
        public string Description { get; set; }
        /// <summary>
        /// 状态
        /// </summary>
        public  int? Status { get; set; }
        /// <summary>
        /// 创建日期
        /// </summary>
        public DateTime? CreateDate { get; set; }
        /// <summary>
        /// 创建人
        /// </summary>
        [StringLength(50)]
        public string CreateName { get; set; }
        /// <summary>
        /// 修改日期
        /// </summary>
        public DateTime? UpdateDate { get; set; }
        /// <summary>
        /// 修改人
        /// </summary>
        [StringLength(50)]
        public string UpdateName { get; set; }
        public DateTime CreationTime { get; set; }
    }
}
3. 基础设施层更新数据库
在基础设施层(即College.EntityFrameworkCore\EntityFrameworkCore\CollegeDbContext.cs)添加一行 public DbSet<Course> courses { get; set; } // 创建 courses 表
using Microsoft.EntityFrameworkCore;
using Abp.Zero.EntityFrameworkCore;
using College.Authorization.Roles;
using College.Authorization.Users;
using College.MultiTenancy;
using College.Entities;
namespace College.EntityFrameworkCore
{
    public class CollegeDbContext : AbpZeroDbContext<Tenant, Role, User, CollegeDbContext>
    {
        /* Define a DbSet for each entity of the application */
        
        public CollegeDbContext(DbContextOptions<CollegeDbContext> options)
            : base(options)
        {
        }
        public DbSet<Course> courses { get; set; }
    }
}
打开程序包管理工具,项目选择 EntityFrameworkCore,依次执行下列命令
Add-Migration 'AddCourse'
Update-Database -Verbose
看到数据库已经有 course 表则说明创建成功


4. 应用层创建应用服务
- 在应用层(即College.Application)下新建Courses文件夹用来存放Course相关服务
- 在Courses文件夹下创建Dto文件夹用来存放数据传输对象
- 创建数据传输对象CourseDto、CreateCourseDto和UpdateCourseDto
4.1 创建数据传输对象
CourseDto
using Abp.Application.Services.Dto;
using Abp.AutoMapper;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace College.Courses.Dto
{
    [AutoMapFrom(typeof(Entities.Course))]
    public class CourseDto : EntityDto<int>
    {
        /// <summary>
        /// 课程编号
        /// </summary>
        [StringLength(50)]
        public string Code { get; set; }
        /// <summary>
        /// 院系编号
        /// </summary>
        [StringLength(50)]
        public string DepartmentCode { get; set; }
        /// <summary>
        /// 课程名称
        /// </summary>
        [StringLength(150)]
        public string Name { get; set; }
        /// <summary>
        /// 课程积分
        /// </summary>
        [Range(0, 5)]
        public int Credits { get; set; }
        /// <summary>
        /// 备注
        /// </summary>
        [StringLength(200)]
        public string Description { get; set; }
        /// <summary>
        /// 状态
        /// </summary>
        public int? Status { get; set; }
        /// <summary>
        /// 创建日期
        /// </summary>
        public DateTime? CreateDate { get; set; }
        /// <summary>
        /// 创建人
        /// </summary>
        [StringLength(50)]
        public string CreateName { get; set; }
        /// <summary>
        /// 修改日期
        /// </summary>
        public DateTime? UpdateDate { get; set; }
        /// <summary>
        /// 修改人
        /// </summary>
        [StringLength(50)]
        public string UpdateName { get; set; }
        public DateTime CreationTime { get; set; }
    }
}
CreateCourseDto
using Abp.Application.Services.Dto;
using Abp.AutoMapper;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace College.Courses.Dto
{
    [AutoMapTo(typeof(Entities.Course))]
    public class CreateCourseDto : EntityDto<int>
    {
        /// <summary>
        /// 课程编号
        /// </summary>
        [StringLength(50)]
        public string Code { get; set; }
        /// <summary>
        /// 院系编号
        /// </summary>
        [StringLength(50)]
        public string DepartmentCode { get; set; }
        /// <summary>
        /// 课程名称
        /// </summary>
        [StringLength(150)]
        public string Name { get; set; }
        /// <summary>
        /// 课程积分
        /// </summary>
        [Range(0, 5)]
        public int Credits { get; set; }
        /// <summary>
        /// 备注
        /// </summary>
        [StringLength(200)]
        public string Description { get; set; }
        /// <summary>
        /// 状态
        /// </summary>
        public int? Status { get; set; }
        /// <summary>
        /// 创建日期
        /// </summary>
        public DateTime? CreateDate { get; set; }
        /// <summary>
        /// 创建人
        /// </summary>
        [StringLength(50)]
        public string CreateName { get; set; }
        /// <summary>
        /// 修改日期
        /// </summary>
        public DateTime? UpdateDate { get; set; }
        /// <summary>
        /// 修改人
        /// </summary>
        [StringLength(50)]
        public string UpdateName { get; set; }
    }
}
UpdateCourse
using Abp.Application.Services.Dto;
using Abp.AutoMapper;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace College.Courses.Dto
{
    [AutoMapTo(typeof(Entities.Course))]
    public class UpdateCourse : EntityDto<int>
    {
        /// <summary>
        /// 课程编号
        /// </summary>
        [StringLength(50)]
        public string Code { get; set; }
        /// <summary>
        /// 院系编号
        /// </summary>
        [StringLength(50)]
        public string DepartmentCode { get; set; }
        /// <summary>
        /// 课程名称
        /// </summary>
        [StringLength(150)]
        public string Name { get; set; }
        /// <summary>
        /// 课程积分
        /// </summary>
        [Range(0, 5)]
        public int Credits { get; set; }
        /// <summary>
        /// 备注
        /// </summary>
        [StringLength(200)]
        public string Description { get; set; }
        /// <summary>
        /// 状态
        /// </summary>
        public int? Status { get; set; }
        /// <summary>
        /// 创建日期
        /// </summary>
        public DateTime? CreateDate { get; set; }
        /// <summary>
        /// 创建人
        /// </summary>
        [StringLength(50)]
        public string CreateName { get; set; }
        /// <summary>
        /// 修改日期
        /// </summary>
        public DateTime? UpdateDate { get; set; }
        /// <summary>
        /// 修改人
        /// </summary>
        [StringLength(50)]
        public string UpdateName { get; set; }
    }
}
4.2 创建应用服务接口
在College.Application\Courses下新建接口ICoursAppService.cs
using Abp.Application.Services;
using College.Courses.Dto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace College.Courses
{
    public interface ICourseAppService : IApplicationService
    {
        /// <summary>
        /// 增加
        /// </summary>
        Task<CourseDto> CreateCourse(CreateCourseDto input);
        /// <summary>
        /// 删除
        /// </summary>
        Task<int> DeleteCourse(IEnumerable<int> ids);
        /// <summary>
        /// 更新
        /// </summary>
        Task<CourseDto> UpdateCourse(UpdateCourseDto input);
        /// <summary>
        /// 查询
        /// </summary>
        Task<CourseDto> GetCourseById(int id);
    }
}
4.3 创建应用服务
在College.Application\Courses下新建类CoursAppService.cs
using Abp.Domain.Repositories;
using Abp.ObjectMapping;
using Abp.Timing;
using College.Courses.Dto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace College.Courses
{
    public class CourseAppService : ICourseAppService
    {
        private readonly IRepository<Entities.Course, int> _repository;
        private readonly IObjectMapper _objectmapper;
        public CourseAppService(IRepository<Entities.Course, int> repository, IObjectMapper objectmapper)
        {
            _repository = repository;
            _objectmapper = objectmapper;
        }
        #region 新增
        public Task<CourseDto> CreateCourse(CreateCourseDto input)
        {
            Entities.Course course = _objectmapper.Map<Entities.Course>(input);
            Entities.Course result = _repository.Insert(course);
            CourseDto output = _objectmapper.Map<CourseDto>(result);
            return Task.FromResult(output);
        }
        #endregion
        #region 删除
        public Task<int> DeleteCourse(IEnumerable<int> ids)
        {
            int num = 0;
            foreach(int id in ids)
            {
                _repository.Delete(id);
                ++num;
            }
            return Task.FromResult(num);
        }
        #endregion
        #region 更新
        public Task<CourseDto> UpdateCourse(UpdateCourseDto input)
        {
            Entities.Course course = _objectmapper.Map<Entities.Course>(input);
            course.UpdateDate = Clock.Now;
            Entities.Course result = _repository.Update(course);
            CourseDto output = _objectmapper.Map<CourseDto>(result);
            return Task.FromResult(output);
        }
        #endregion
        #region 查询
        public Task<CourseDto> GetCourseById(int id)
        {
            Entities.Course result = _repository.Get(id);
            CourseDto output = _objectmapper.Map<CourseDto>(result);
            return Task.FromResult(output);
        }
        #endregion
    }
}
4.4 创建映射文件
在College.Application\Courses下新建类CollegeApplicationAutoMapperProfile.cs
using AutoMapper;
using College.Courses.Dto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace College
{
    public class CollegeApplicationAutoMapperProfile : Profile
    {
        public CollegeApplicationAutoMapperProfile()
        {
            CreateMap<Entities.Course, CourseDto>();
        }
    }
}
最后展示一下应用层的目录结构

5. 展示层创建控制器
在College.Web.Mvc\Controllers下新增类CoursesController.cs
using Abp.AspNetCore.Mvc.Controllers;
using College.Courses;
using College.Courses.Dto;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace College.Web.Controllers
{
    public class CoursesController : AbpController
    {
        private readonly ICourseAppService _courseAppService;
        public CoursesController(ICourseAppService courseAppService)
        {
            _courseAppService = courseAppService;
        }
        [HttpPost]
        public async Task<CourseDto> CreateCourseAsync(CreateCourseDto courseDto)
        {
            return await _courseAppService.CreateCourse(courseDto);
        }
        [HttpDelete]
        public async Task<int> DeleteCourseAsync(IEnumerable<int> ids)
        {
            return await _courseAppService.DeleteCourse(ids);
        }
        [HttpPost]
        public async Task<CourseDto> UpdateCourseAsync(UpdateCourseDto courseDto)
        {
            return await _courseAppService.UpdateCourse(courseDto);
        }
        [HttpPost]
        public async Task<CourseDto> GetCourseAsync(int id)
        {
            return await _courseAppService.GetCourseById(id);
        }
    }
}
6. 验收成果
- 检查
 设置College.Web.Host为启动项,运行
 出现下图的结果说明正确
  
- 添加数据
  
 状态码为200,说明添加数据成功,进入数据库查看一下
  
 数据库已有数据。
- 查询数据
  
 查询成功,返回数据
  
- 更新数据
  
- 删除数据
  
7. 改进
如果大家留意的话就会发现,在更新的时候可以把创建人和创建时间也更新掉,这显然不是我们想要的。而且更新时间 和 修改人也不需要暴露出来。另外大家应该也注意到一开始我们定义了 Course 这个实体,后面我们又创建了三个数据传输对象Dto,里面的数据类型及个数都是一样的,这不是多次一举吗?
不是的,如果我们在 UpdateCourseDto 中去掉 CreateDate、CreateName,刚刚提到的问题就解决了。
Dto 起到不暴露所有数据的作用,同时也可以起到数据验证的作用。
将 UpdateCourseDto.cs 改为:
using Abp.Application.Services.Dto;
using Abp.AutoMapper;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace College.Courses.Dto
{
    [AutoMapTo(typeof(Entities.Course))]
    public class UpdateCourseDto : EntityDto<int>
    {
        /// <summary>
        /// 课程编号
        /// </summary>
        [StringLength(50)]
        public string Code { get; set; }
        /// <summary>
        /// 院系编号
        /// </summary>
        [StringLength(50)]
        public string DepartmentCode { get; set; }
        /// <summary>
        /// 课程名称
        /// </summary>
        [StringLength(150)]
        public string Name { get; set; }
        /// <summary>
        /// 课程积分
        /// </summary>
        [Range(0, 5)]
        public int Credits { get; set; }
        /// <summary>
        /// 备注
        /// </summary>
        [StringLength(200)]
        public string Description { get; set; }
        /// <summary>
        /// 状态
        /// </summary>
        public int? Status { get; set; }
    }
}
再次启动项目查看效果

可以发现 CreateName 和 CreateDate,UpdateName 和 UpdateDate 已经不见了。
其实简单增删改查我们可以不用自己实现,我们只需要提供数据传输对象,再利用框架本身的AsyncCrudAppService即可。这里为了能够更好地厘清框架逻辑才自己实现。
 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号