最近没有更新 ABP 框架的相关文章, 一直在研究和封装相关的接口, 总算告一段落, 开始继续整理下开发心得. 上次我在随笔《ABP 开发框架前后端开发系列 ---(5)web API 调用类在 Winform 项目中的使用》中介绍了字典模块的管理, 以及实现了常规的获取所有记录, 获取条件查询记录, 创建, 更新, 删除这些接口. 本篇继续深入介绍 ABP 框架在实际项目中使用的情况, 本篇随笔整理对 ABP 基础接口, 以及展示完成的省份城市行政区管理模块的内容.
1,ABP 常规处理接口
根据 ABP 框架默认提供的一些接口, 我们可以在服务端封装好相关的 Web API 接口(由于动态 API 的便利, 其实是完成 ApplicationService 层即可), 前面介绍了获取条件查询记录, 创建, 更新, 删除这些接口的实现和处理, 以及可以扩展自己的自定义业务接口, 如下是字典模块的接口关系.
字典管理界面, 列出字典类型, 并对字典类型下的字典数据进行分页展示, 分页展示利用分页控件展示.
新增或者编辑窗体界面如下
或者是批量的字典数据录入
这个精确或者模糊查询, 则是在应用服务层里面定义规则的, 在应用服务层接口类里面, 重写 CreateFilteredQuery 可以设置 GetAll 的查询规则, 重写 ApplySorting 则可以指定列表的排序顺序.
2,ABP 常规查询接口的细化
在前面介绍了的内容汇总, 基本上实现了常规数据的分页查询, 我们可以看到, 对于字典数据来说, 分页查询条件是在 DictDataPagedDto 里面定义, 这个是我们定义的分页条件, 如下代码所示.
- /// <summary>
- /// 用于根据条件分页查询
- /// </summary>
- public class DictDataPagedDto : PagedResultRequestDto
- {
- /// <summary>
- /// 字典类型 ID
- /// </summary>
- public virtual string DictType_ID { get; set; }
- /// <summary>
- /// 类型名称
- /// </summary>
- public virtual string Name { get; set; }
- /// <summary>
- /// 指定值
- /// </summary>
- public virtual string Value { get; set; }
- /// <summary>
- /// 备注
- /// </summary>
- public virtual string Remark { get; set; }
- }
这个类文件, 我们一般把这个业务模块相关的统一放在一个文件中, 例如字典数据相关的 DTO 放在一个 DictDataDto 文件里面, 方便管理, 如下所示.
上面是字典模块的一些基础介绍, 实际上我们开发业务模块的时候, 录入数据的时候, 还需要一个判断的步骤, 如不允许名称重复的情况. 在创建新的记录和更新已有记录都需要进行必要的判断, 保证数据的有效性和不重复性.
如对于省份管理界面来说, 我们不能运行重复录入省份名称, 那么就需要在录入数据或者更新数据的时候, 进行必要的存在性判断.
那么上面的处理是如何实现的呢.
主要的界面实现代码如下所示.
- if (string.IsNullOrEmpty(ID))
- {
- // 判断存在条件
- var countDto = new ProvincePagedDto() { ProvinceName = this.txtProvince.Text };
- bool isExist = await ProvinceApiCaller.Instance.Count(countDto)> 0;
- if (isExist)
- {
- MessageDxUtil.ShowTips("省份名称已存在, 请选择其他名称");
- this.txtProvince.Focus();
- return;
- }
- else
- {
- // 创建新记录
- tempInfo = await ProvinceApiCaller.Instance.Create(tempInfo);
- }
- }
- else
- {
- // 判断存在条件, 排除本记录同名情况
- var countDto = new ProvincePagedDto() { ProvinceName = this.txtProvince.Text, ExcludeId = ID.ToInt64() };
- bool isExist = await ProvinceApiCaller.Instance.Count(countDto)> 0;
- if (isExist)
- {
- MessageDxUtil.ShowTips("省份名称已存在, 请选择其他名称");
- this.txtProvince.Focus();
- return;
- }
- else
- {
- // 更新记录
- tempInfo = await ProvinceApiCaller.Instance.Update(tempInfo);
- }
- }
- ProcessDataSaved(this.btnOK, new EventArgs());
- this.DialogResult = System.Windows.Forms.DialogResult.OK;
我们发现, 这里增加了一个 Count 的函数用来判断, 传入的条件就是前面的分页请求条件.
bool isExist = await ProvinceApiCaller.Instance.Count(countDto)> 0;
我们看看我们的应用服务层的接口实现如下所示.
- /// <summary>
- /// 获取指定条件的数量
- /// </summary>
- /// <param name="input">查找条件</param>
- /// <returns></returns>
- public async virtual Task<int> Count(TGetAllInput input)
- {
- var query = CreateFilteredQuery(input);
- return await Task.FromResult(query.Count());
- }
这里最终还是跳转到 CreateFilteredQuery 函数里面实现判断逻辑了.
- /// <summary>
- /// 自定义条件处理
- /// </summary>
- /// <param name="input">查询条件 Dto</param>
- /// <returns></returns>
- protected override IQueryable<Province> CreateFilteredQuery(ProvincePagedDto input)
- {
- return base.CreateFilteredQuery(input)
- .WhereIf(input.ExcludeId.HasValue, t=>t.Id != input.ExcludeId) // 不包含排除 ID
- .WhereIf(!input.ProvinceName.IsNullOrWhiteSpace(), t => t.ProvinceName.Contains(input.ProvinceName));
- }
这里面包含了两个判断条件, 一个是排除指定的 ID 记录, 一个是匹配省份名称.
因为我们在更新记录的时候, 需要判断非本记录是否有重复的名称.
- // 判断存在条件, 排除本记录同名情况
- var countDto = new ProvincePagedDto() {
- ProvinceName = this.txtProvince.Text, ExcludeId = ID.ToInt64()
- };
- bool isExist = await ProvinceApiCaller.Instance.Count(countDto)> 0;
这个 ExcludeId 我们在分页条件里面增加一个固定的属性即可.
以上的分页信息, 包含了实体 DTO 对象的一些属性, 我们可以根据需要增加或者减少一部分属性.
另外我们定义的创建省份 Dto 对象和获取到单个实体的 DTO 对象, 他们的定义和关系如下所示, 方便我们在界面上进行操作.
- /// <summary>
- /// 创建全国省份表, DTO 对象
- /// </summary>
- public class CreateProvinceDto : EntityDto<long>
- {
- /// <summary>
- /// 默认构造函数(需要初始化属性的在此处理)
- /// </summary>
- public CreateProvinceDto()
- {
- }
- #region Property Members
- /// <summary>
- /// 省份名称
- /// </summary>
- [Required]
- public virtual string ProvinceName { get; set; }
- #endregion
- }
- /// <summary>
- /// 全国省份表, DTO 对象
- /// </summary>
- public class ProvinceDto : CreateProvinceDto
- {
- }
固定这些规则后, 我们也可以用代码生成工具快速生成对应的 DTO 文件了.
有了这些分页属性后, 我们就可以在应用服务层里面定义自己的过滤规则了, 如对于字典类型的应用服务层的筛选条件函数, 如下所示.
- /// <summary>
- /// 自定义条件处理
- /// </summary>
- /// <param name="input"></param>
- /// <returns></returns>
- protected override IQueryable<DictType> CreateFilteredQuery(DictTypePagedDto input)
- {
- return base.CreateFilteredQuery(input)
- .WhereIf(!string.IsNullOrEmpty(input.ExcludeId), t => t.Id != input.ExcludeId) // 不包含排除 ID
- .WhereIf(!string.IsNullOrEmpty(input.Name), t => t.Name.Contains(input.Name))
- .WhereIf(!string.IsNullOrEmpty(input.Remark), t => t.Remark.Contains(input.Remark))
- .WhereIf(!string.IsNullOrEmpty(input.Code), t => t.Code == input.Code)
- .WhereIf(!string.IsNullOrEmpty(input.PID), t => t.PID == input.PID);
- }
上面是对于包含, 相等或者不等于的三种情况的条件判断, 如果我们还需要一个时间区间范围或者数值范围的判断, 那么同样可以在这里进行管理规则, 如下是针对产品应用服务层的过滤规则, 如下代码所示.
- /// <summary>
- /// 自定义条件处理
- /// </summary>
- /// <param name="input">查询条件 Dto</param>
- /// <returns></returns>
- protected override IQueryable<Product> CreateFilteredQuery(ProductPagedDto input)
- {
- return base.CreateFilteredQuery(input)
- .WhereIf(!input.ExcludeId.IsNullOrWhiteSpace(), t => t.Id != input.ExcludeId) // 不包含排除 ID
- .WhereIf(!input.ProductNo.IsNullOrWhiteSpace(), t => t.ProductNo.Contains(input.ProductNo)) // 如需要精确匹配则用 Equals
- .WhereIf(!input.BarCode.IsNullOrWhiteSpace(), t => t.BarCode.Contains(input.BarCode)) // 如需要精确匹配则用 Equals
- .WhereIf(!input.MaterialCode.IsNullOrWhiteSpace(), t => t.MaterialCode.Contains(input.MaterialCode)) // 如需要精确匹配则用 Equals
- .WhereIf(!input.ProductType.IsNullOrWhiteSpace(), t => t.ProductType.Contains(input.ProductType)) // 如需要精确匹配则用 Equals
- .WhereIf(!input.ProductName.IsNullOrWhiteSpace(), t => t.ProductName.Contains(input.ProductName)) // 如需要精确匹配则用 Equals
- .WhereIf(!input.Unit.IsNullOrWhiteSpace(), t => t.Unit.Contains(input.Unit)) // 如需要精确匹配则用 Equals
- .WhereIf(!input.Note.IsNullOrWhiteSpace(), t => t.Note.Contains(input.Note)) // 如需要精确匹配则用 Equals
- .WhereIf(!input.Description.IsNullOrWhiteSpace(), t => t.Description.Contains(input.Description)) // 如需要精确匹配则用 Equals
- // 状态
- .WhereIf(input.Status.HasValue, t => t.Status==input.Status)
- // 成本价区间查询
- .WhereIf(input.PriceStart.HasValue, s => s.Price>= input.PriceStart.Value)
- .WhereIf(input.PriceEnd.HasValue, s => s.Price <= input.PriceEnd.Value)
- // 销售价区间查询
- .WhereIf(input.SalePriceStart.HasValue, s => s.SalePrice>= input.SalePriceStart.Value)
- .WhereIf(input.SalePriceEnd.HasValue, s => s.SalePrice <= input.SalePriceEnd.Value)
- // 特价区间查询
- .WhereIf(input.SpecialPriceStart.HasValue, s => s.SpecialPrice>= input.SpecialPriceStart.Value)
- .WhereIf(input.SpecialPriceEnd.HasValue, s => s.SpecialPrice <= input.SpecialPriceEnd.Value)
- .WhereIf(input.IsUseSpecial.HasValue, t => t.IsUseSpecial == input.IsUseSpecial) // 如需要精确匹配则用 Equals
- // 最低折扣区间查询
- .WhereIf(input.LowestDiscountStart.HasValue, s => s.LowestDiscount>= input.LowestDiscountStart.Value)
- .WhereIf(input.LowestDiscountEnd.HasValue, s => s.LowestDiscount <= input.LowestDiscountEnd.Value)
- // 创建日期区间查询
- .WhereIf(input.CreationTimeStart.HasValue, s => s.CreationTime>= input.CreationTimeStart.Value)
- .WhereIf(input.CreationTimeEnd.HasValue, s => s.CreationTime <= input.CreationTimeEnd.Value);
- }
以上就是我们深入对分页查询和判断是否存在接口的细节处理, 可以包含很多自定义的条件, 如等于或不等于, 包含或者不包含, 区间查询 (大于或者小于等) 条件的处理. 对于省份城市行政区管理模块的重复性判断, 我们通过 Count 函数来判断, 同时在后台应用服务层对这些参数进行规则过滤即可.
来源: https://www.cnblogs.com/wuhuacong/p/11017095.html