上一篇文章中介绍了如何下载、运行ABP Zero示例项目,这个示例项目可以直接作为模板进行二次开发,很适合做企业开发框架。

本未介绍基于ABP Zero示例项目,如何新建一个自定义的实体。

此处以EquipmentType(设备类型)为例,建立一个简单的实体。

以下是之前添加一个简单实体类的效果:

主页

【ABP】从零开始学习ABP_001_新建实体功能

 公司列表

【ABP】从零开始学习ABP_001_新建实体功能

 新建公司

【ABP】从零开始学习ABP_001_新建实体功能

编辑公司

【ABP】从零开始学习ABP_001_新建实体功能

删除公司

【ABP】从零开始学习ABP_001_新建实体功能


建立方法

按ABP的标准格式,完整建立一个实体,映射到数据库,作为一个基础资料,并通过前端进行增删查改功能,最小步骤如下:

  1. 新增实体类。
  2. 添加数据库映射。
  3. 同步到数据库。
  4. 新建Dto,建立Dto与实体类的映射。
  5. 建立实体类的功能接口(增删查改),并实现接口。
  6. 添加视图模型ViewModel。
  7. 添加控制器。
  8. 添加视图。
  9. 添加js脚本。
  10. 添加前端菜单(入口)。
  11. 添加权限管理。

1. 添加实体类

在core层添加实体类

1 namespace oMES_APServer.Equipments
2 {
3     public class EquipmentType : MESBase.MESBaseEntity
4     {
5 
6     }
7 }

这里我把MES会用到的常规属性单独建了一个基类,并继承ABP全属性基类FullAuditedEntity

 1 using Abp.Domain.Entities;
 2 using Abp.Domain.Entities.Auditing;
 3 using System.ComponentModel.DataAnnotations;
 4 
 5 namespace oMES_APServer.MESBase
 6 {
 7     public class MESBaseEntity : FullAuditedEntity<int>, IPassivable
 8     {
 9 
10         public const int MaxCodeLength = 64;
11         public const int MaxNameLength = 256;
12         public const int MaxBriefNameLength = 64;
13         public const int MaxRemarkLength = 64;
14 
15         [Required]
16         [StringLength(MaxCodeLength)]
17         public string Code { get; set; }
18 
19         [Required]
20         [StringLength(MaxNameLength)]
21         public string Name { get; set; }
22 
23         [StringLength(64)]
24         public string BriefName { get; set; }
25 
26         [StringLength(512)]
27         public string Remark { get; set; }
28         public bool IsActive { get; set; }
29 
30     }
31 }

2. 添加数据库映射

在EFCore层添加以下代码:为了让EFCore能够自动生成数据库表结构。

public DbSet<EquipmentType> equipmentTypes { get; set; }

 

modelBuilder.Entity<EquipmentType>().ToTable("EquipmentType").HasAlternateKey(x => x.Code).HasName("UK_EquipmentType_Code");

其中.HasAlternateKey表示建立唯一键,.HasName表示键的名称。

 

 1 using Microsoft.EntityFrameworkCore;
 2 using Abp.Zero.EntityFrameworkCore;
 3 using oMES_APServer.Authorization.Roles;
 4 using oMES_APServer.Authorization.Users;
 5 using oMES_APServer.MultiTenancy;
 6 using oMES_APServer.Companies;
 7 using oMES_APServer.Equipments;
 8 
 9 namespace oMES_APServer.EntityFrameworkCore
10 {
11     public class oMES_APServerDbContext : AbpZeroDbContext<Tenant, Role, User, oMES_APServerDbContext>
12     {
13         /* Define a DbSet for each entity of the application */
14         public oMES_APServerDbContext(DbContextOptions<oMES_APServerDbContext> options)
15             : base(options)
16         {
17         }
18 
19         /*添加自定义实体类的数据库映射*/
20         public DbSet<Company> companies { get; set; }
21         public DbSet<Department> departments { get; set; }
22         public DbSet<EquipmentType> equipmentTypes { get; set; }
23 
24         protected override void OnModelCreating(ModelBuilder modelBuilder)
25         {
26             base.OnModelCreating(modelBuilder);
27 
28             modelBuilder.Entity<Company>().ToTable("Company")
29                 .HasAlternateKey(x=>x.Code).HasName("UK_Company_Code");
30             modelBuilder.Entity<Department>().ToTable("Department")
31                 .HasAlternateKey(x => x.Code).HasName("UK_Department_Code");
32             modelBuilder.Entity<EquipmentType>().ToTable("EquipmentType")
33                 .HasAlternateKey(x => x.Code).HasName("UK_EquipmentType_Code");
34         }
35 
36     }
37 }

3. 同步到数据库。

依次执行以下两条指令:

  1. add-migration add-EquipmentType
  2. update-database

第一条表示,新建同步映射,映射名称为add-EquipmentType。

第二条表示同步数据库。

【ABP】从零开始学习ABP_001_新建实体功能 

执行时,出现上述报错,不要慌,重新生成一下项目,应该有报错,解决完项目报错再执行,就可以了。

执行时注意默认项目需要选择EFCore项目。

4. 新建Dto,建立Dto与实体类的映射

在Application层建立Dto

1 namespace oMES_APServer.Equipments.Dto
2 {
3     public class EquipmentTypeDto : MESBaseDto.MESBaseEntityDto
4     {
5 
6     }
7 }

在Dtos同级目录下,建立实体类和Dto的映射 EquipmentTypeMapProfile

 1 using AutoMapper;
 2 
 3 namespace oMES_APServer.Equipments.Dto
 4 {
 5     public class EquipmentTypeMapProfile : Profile
 6     {
 7         public EquipmentTypeMapProfile()
 8         {
 9             CreateMap<EquipmentType, EquipmentTypeDto>();
10             CreateMap<EquipmentTypeDto, EquipmentType>();
11             CreateMap<EquipmentTypeDto, EquipmentType>()
12                 .ForMember(x => x.CreationTime, opt => opt.Ignore());
13         }
14     }
15 }

引申:

ABP Zero的Dto类文件夹中,会有一个 PagedxxxResultRequestDto.cs类,这个是用于按给定条件查找数据列表,并进行分页的类。目前还没有学习到,所以仅按照现有的实体类进行仿照编程,不做额外处理。

 1 using Abp.Application.Services.Dto;
 2 
 3 namespace oMES_APServer.Equipments.Dto
 4 {
 5     public class PagedEquipmentTypeResultRequestDto : PagedResultRequestDto
 6     {
 7 
 8         public string Code { get; set; }
 9 
10         public string Name { get; set; }
11 
12         public bool? IsActive { get; set; }
13 
14     }
15 }

5. 建立实体类的功能接口(增删查改),并实现接口。

在Application层中建立接口:因为ABP接口已经实现了基础功能(增删查改)的方法定义,所以直接继承IAsyncCrudAppService即可,写法参照已有的User相关类。

 1 using Abp.Application.Services;
 2 using oMES_APServer.Companies.Dto;
 3 
 4 namespace oMES_APServer.Equipments
 5 {
 6     public interface IEquipmentTypesAppService : IAsyncCrudAppService<CompanyDto, int, PagedCompanyResultRequestDto, CompanyDto, CompanyDto>
 7     {
 8 
 9     }
10 }

自定义实现基础接口:

 1 using System.Collections.Generic;
 2 using System.Linq;
 3 using System.Threading.Tasks;
 4 using Abp.Application.Services;
 5 using Abp.Application.Services.Dto;
 6 using Abp.Domain.Repositories;
 7 using Abp.Domain.Uow;
 8 using Abp.Extensions;
 9 using Abp.Linq.Extensions;
10 using Abp.UI;
11 using Microsoft.EntityFrameworkCore;
12 using oMES_APServer.Equipments.Dto;
13 
14 namespace oMES_APServer.Equipments
15 {
16     public class EquipmentTypesAppService : AsyncCrudAppService<EquipmentType, EquipmentTypeDto, int, PagedEquipmentTypeResultRequestDto, EquipmentTypeDto, EquipmentTypeDto>, IEquipmentTypeAppService
17     {
18         private readonly IRepository<EquipmentType> _equipmentTypeRepository;
19 
20         public EquipmentTypesAppService(IRepository<EquipmentType> equipmentTypeRepository)
21             : base(equipmentTypeRepository)
22         {
23             _equipmentTypeRepository = equipmentTypeRepository;
24 
25             LocalizationSourceName = oMES_APServerConsts.LocalizationSourceName;
26         }
27 
28         public override async Task<EquipmentTypeDto> Get(EntityDto<int> input)
29         {
30             var equipmentType = await _equipmentTypeRepository
31                 .GetAsync(input.Id);
32 
33             return ObjectMapper.Map<EquipmentTypeDto>(equipmentType);
34         }
35 
36         public override async Task<PagedResultDto<EquipmentTypeDto>> GetAll(PagedEquipmentTypeResultRequestDto input)
37         {
38             var equipmentTypes = Repository.GetAll()
39                 .WhereIf(!input.Name.IsNullOrWhiteSpace(), x => x.Name.Contains(input.Name))
40                 .WhereIf(!input.Code.IsNullOrWhiteSpace(), x => x.Code.Contains(input.Code))
41                 .WhereIf(input.IsActive.HasValue, x => x.IsActive == input.IsActive)
42                 .OrderBy(x => x.Code);
43 
44             return await Task.FromResult(new PagedResultDto<EquipmentTypeDto>(equipmentTypes.CountAsync().Result,
45                 ObjectMapper.Map<List<EquipmentTypeDto>>(equipmentTypes)
46             ));
47         }
48 
49         public override async Task<EquipmentTypeDto> Create(EquipmentTypeDto input)
50         {
51 
52             //判断CODE是否已存在
53             var model = await Repository.GetAllIncluding().FirstOrDefaultAsync(x => x.Code == input.Code);
54             if (model != null)
55             {
56                 throw new UserFriendlyException(L("EquipmentType code already exists"));
57             }
58 
59             //检查是否已被软删除,已经软删除的数据,无法通过
60             using (UnitOfWorkManager.Current.DisableFilter(AbpDataFilters.SoftDelete))
61             {
62                 //判断CODE是否已存在
63                 var model0 = await Repository.GetAllIncluding().FirstOrDefaultAsync(x => x.Code == input.Code);
64                 if (model0 != null)
65                 {
66                     throw new UserFriendlyException(L("EquipmentType code is deleted"));
67                 }
68             }
69 
70             var entity = ObjectMapper.Map<EquipmentType>(input);
71             await _equipmentTypeRepository.InsertAsync(entity);
72             return MapToEntityDto(entity);
73         }
74 
75         public override async Task<EquipmentTypeDto> Update(EquipmentTypeDto input)
76         {
77             var entity = await _equipmentTypeRepository.GetAsync(input.Id);
78 
79             ObjectMapper.Map(input, entity);
80 
81             await _equipmentTypeRepository.UpdateAsync(entity);
82 
83             return MapToEntityDto(entity);
84         }
85 
86         public override async Task Delete(EntityDto<int> input)
87         {
88             var entity = await _equipmentTypeRepository.GetAsync(input.Id);
89             await _equipmentTypeRepository.DeleteAsync(entity);
90         }
91 
92     }
93 }

其中,创建的代码如下:在基类中,我们设置了Code字段为唯一键,所以需要先确定Code不存在

 1 public override async Task<EquipmentTypeDto> Create(EquipmentTypeDto input)
 2         {
 3 
 4             //判断CODE是否已存在
 5             var model = await Repository.GetAllIncluding().FirstOrDefaultAsync(x => x.Code == input.Code);
 6             if (model != null)
 7             {
 8                 throw new UserFriendlyException(L("EquipmentType code already exists"));
 9             }
10 
11             //检查是否已被软删除,已经软删除的数据,无法通过
12             using (UnitOfWorkManager.Current.DisableFilter(AbpDataFilters.SoftDelete))
13             {
14                 //判断CODE是否已存在
15                 var model0 = await Repository.GetAllIncluding().FirstOrDefaultAsync(x => x.Code == input.Code);
16                 if (model0 != null)
17                 {
18                     throw new UserFriendlyException(L("EquipmentType code is deleted"));
19                 }
20             }
21 
22             var entity = ObjectMapper.Map<EquipmentType>(input);
23             await _equipmentTypeRepository.InsertAsync(entity);
24             return MapToEntityDto(entity);
25         }

ABP的全属性类FullAuditedEntity中,预置了软删除功能,如果一条数据被软删除了(IsDeleted字段为1),那么直接查找是招不到的。

需要临时关闭软删除的过滤器,才能找到:在using中,使用正常的查询代码,就能查到已被软删除的数据。

 1 //检查是否已被软删除,已经软删除的数据,无法通过
 2             using (UnitOfWorkManager.Current.DisableFilter(AbpDataFilters.SoftDelete))
 3             {
 4                 //判断CODE是否已存在
 5                 var model0 = await Repository.GetAllIncluding().FirstOrDefaultAsync(x => x.Code == input.Code);
 6                 if (model0 != null)
 7                 {
 8                     throw new UserFriendlyException(L("EquipmentType code is deleted"));
 9                 }
10             }

6. 添加视图模型ViewModel。

 在MVC层

添加查询视图模型,这里没有做特殊处理,直接使用Dto

 1 using oMES_APServer.Equipments.Dto;
 2 using System.Collections.Generic;
 3 
 4 namespace oMES_APServer.Web.Models.EquipmentTypes
 5 {
 6     public class EquipmentTypeListViewModel
 7     {
 8         public IReadOnlyList<EquipmentTypeDto> EquipmentTypes { get; set; }
 9     }
10 }

添加编辑视图模型

1 using oMES_APServer.Equipments.Dto;
2 
3 namespace oMES_APServer.Web.Models.EquipmentTypes
4 {
5     public class EditEquipmentTypeModalViewModel
6     {
7         public EquipmentTypeDto EquipmentType { get; set; }
8     }
9 }

 7. 添加控制器。

控制器中添加两个方法Index和Edit.

 1 using System.Threading.Tasks;
 2 using Abp.Application.Services.Dto;
 3 using Microsoft.AspNetCore.Mvc;
 4 using oMES_APServer.Controllers;
 5 using oMES_APServer.Equipments;
 6 using oMES_APServer.Equipments.Dto;
 7 using oMES_APServer.Web.Models.EquipmentTypes;
 8 
 9 namespace oMES_APServer.Web.Mvc.Controllers
10 {
11     public class EquipmentTypesController : oMES_APServerControllerBase
12     {
13 
14         private readonly IEquipmentTypeAppService _equipmentTypeAppService;
15 
16         public EquipmentTypesController(IEquipmentTypeAppService equipmentTypeAppService)
17         {
18             _equipmentTypeAppService = equipmentTypeAppService;
19         }
20 
21         public async Task<IActionResult> Index()
22         {
23             var modelDto = (await _equipmentTypeAppService
24                 .GetAll(new PagedEquipmentTypeResultRequestDto { MaxResultCount = int.MaxValue })
25                 ).Items;
26             var viewModel = new EquipmentTypeListViewModel
27             {
28                 EquipmentTypeList = modelDto
29             };
30             return View(viewModel);
31         }
32 
33         public async Task<ActionResult> EditEquipmentTypeModal(int id)
34         {
35             var modelDto = await _equipmentTypeAppService.Get(new EntityDto(id));
36             var editViewModel = new EditEquipmentTypeModalViewModel
37             {
38                 EquipmentType = modelDto
39             };
40             return View("_EditEquipmentTypeModal", editViewModel);
41         }
42 
43     }
44 }

8. 添加视图。

视图由user中相关视图复制而来,将名称user更改为equipmentType相关即可使用。

查看&新建视图

  1 @using oMES_APServer.Web.Startup
  2 @using oMES_APServer.Equipments
  3 @model oMES_APServer.Web.Models.EquipmentTypes.EquipmentTypeListViewModel
  4 @{
  5     ViewBag.CurrentPageName = PageNames.EquipmentTypes;
  6 }
  7 @section scripts
  8 {
  9     <environment names="Development">
 10         <script src="~/view-resources/Views/EquipmentTypes/Index.js" asp-append-version="true"></script>
 11     </environment>
 12 
 13     <environment names="Staging,Production">
 14         <script src="~/view-resources/Views/EquipmentTypes/Index.js" asp-append-version="true"></script>
 15     </environment>
 16 }
 17 <div class="row clearfix">
 18     <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
 19         <div class="card">
 20             <div class="header">
 21                 <h2>
 22                     @L("EquipmentType")
 23                 </h2>
 24                 <ul class="header-dropdown m-r--5">
 25                     <li class="dropdown">
 26                         <a href="javascript:void(0);" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
 27                             <i class="material-icons">more_vert</i>
 28                         </a>
 29                         <ul class="dropdown-menu pull-right">
 30                             <li><a id="RefreshButton" href="javascript:void(0);" class="waves-effect waves-block"><i class="material-icons">refresh</i>@L("Refresh")</a></li>
 31                         </ul>
 32                     </li>
 33                 </ul>
 34             </div>
 35             <div class="body table-responsive">
 36                 <table class="table">
 37                     <thead>
 38                         <tr>
 39                             <th>@L("Code")</th>
 40                             <th>@L("Name")</th>
 41                             <th>@L("BriefName")</th>
 42                             <th>@L("Remark")</th>
 43                             <th>@L("CreationTime")</th>
 44                             <th>@L("CreatorUserId")</th>
 45                             <th>@L("LastModificationTime")</th>
 46                             <th>@L("LastModifierUserId")</th>
 47                         </tr>
 48                     </thead>
 49                     <tbody>
 50                         @foreach (var viewModel in Model.EquipmentTypeList)
 51                         {
 52                             <tr>
 53                                 <td>@viewModel.Code</td>
 54                                 <td>@viewModel.Name</td>
 55                                 <td>@viewModel.BriefName</td>
 56                                 <td>@viewModel.Remark</td>
 57                                 <td>@viewModel.CreationTime</td>
 58                                 <td>@viewModel.CreatorUserId</td>
 59                                 <td>@viewModel.LastModificationTime</td>
 60                                 <td>@viewModel.LastModifierUserId</td>
 61 
 62                                 <td class="dropdown">
 63                                     <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
 64                                         <i class="material-icons">menu</i>
 65                                     </a>
 66                                     <ul class="dropdown-menu pull-right">
 67                                         <li><a href="#" class="waves-effect waves-block edit-equipmentType" data-equipmentType-id="@viewModel.Id" data-toggle="modal" data-target="#EquipmentTypeEditModal"><i class="material-icons">edit</i>@L("Edit")</a></li>
 68                                         <li><a href="#" class="waves-effect waves-block delete-equipmentType" data-equipmentType-id="@viewModel.Id" data-equipmentType-name="@viewModel.Name"><i class="material-icons">delete_sweep</i>@L("Delete")</a></li>
 69                                     </ul>
 70                                 </td>
 71                             </tr>
 72                         }
 73                     </tbody>
 74                 </table>
 75                 <button type="button" class="btn btn-primary btn-circle waves-effect waves-circle waves-float pull-right" data-toggle="modal" data-target="#EquipmentTypeCreateModal">
 76                     <i class="material-icons">add</i>
 77                 </button>
 78             </div>
 79         </div>
 80     </div>
 81 </div>
 82 
 83 <div class="modal fade" id="EquipmentTypeCreateModal" tabindex="-1" role="dialog" aria-labelledby="EquipmentTypeCreateModalLabel" data-backdrop="static">
 84     <div class="modal-dialog" role="document">
 85         <div class="modal-content">
 86             <form name="equipmentTypeCreateForm" role="form" novalidate class="form-validation">
 87                 <div class="modal-header">
 88                     <h4 class="modal-title">
 89                         <span>@L("CreateNewEquipmentType")</span>
 90                     </h4>
 91                 </div>
 92                 <div class="modal-body">
 93                     <div class="form-group form-float">
 94                         <div class="form-line">
 95                             <input class="form-control" type="text" name="Code" required maxlength="@EquipmentType.MaxCodeLength">
 96                             <label class="form-label">@L("Code")</label>
 97                         </div>
 98                     </div>
 99                     <div class="form-group form-float">
100                         <div class="form-line">
101                             <input class="form-control" type="text" name="Name" required maxlength="@EquipmentType.MaxNameLength">
102                             <label class="form-label">@L("Name")</label>
103                         </div>
104                     </div>
105                     <div class="form-group form-float">
106                         <div class="form-line">
107                             <input class="form-control" type="text" name="BriefName" maxlength="@EquipmentType.MaxBriefNameLength">
108                             <label class="form-label">@L("BriefName")</label>
109                         </div>
110                     <div class="form-group form-float">
111                         <div class="form-line">
112                             <input class="form-control" type="text" name="Remark" maxlength="@EquipmentType.MaxRemarkLength">
113                             <label class="form-label">@L("Remark")</label>
114                         </div>
115                     </div>
116                 </div>
117                 <div class="modal-footer">
118                     <button type="button" class="btn btn-default waves-effect" data-dismiss="modal">@L("Cancel")</button>
119                     <button type="submit" class="btn btn-primary waves-effect">@L("Save")</button>
120                 </div>
121             </form>
122         </div>
123     </div>
124 </div>
125 
126 <div class="modal fade" id="EquipmentTypeEditModal" tabindex="-1" role="dialog" aria-labelledby="EquipmentTypeEditModalLabel" data-backdrop="static">
127     <div class="modal-dialog" role="document">
128         <div class="modal-content">
129 
130         </div>
131     </div>
132 </div>
Index.cshtml

相关文章: