• 学ABP(一)——看文档写第一段代码


      看了整整周末两天的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,如何事务处理等。
      好了,希望对大家有所帮助,美好的一周即将开始,祝大家工作、学习愉快~~~~

      链接本文:https://www.cnblogs.com/quluqi/p/13341318.html

  • 相关阅读:
    条码解析的一片js
    再看.net本质(二)
    再看.net本质
    powerdesigner逆向导出oracle数据库结构显示备注
    powerdesigner逆向工程,从数据库导出PDM
    实现HTTP跳转到HTTPS
    opencart 模块开发详解
    Opencart 之 Registry 类详解
    OpenCart 之registry功用
    php+支付宝整合
  • 原文地址:https://www.cnblogs.com/quluqi/p/13341318.html
Copyright © 2020-2023  润新知