在Asp.net Web API中,对业务数据的分页查询处理是一个非常常见的接口,我们需要在查询条件对象中,定义好相应业务的查询参数,排序信息,请求记录数和每页大小信息等内容,根据这些查询信息,我们在后端的Asp.net Web API中实现对这些数据的按需获取,并排序返回给客户端使用。本篇随笔介绍利用查询条件对象,在Asp.net Web API中实现对业务数据的分页查询处理。

1、Web API控制器基类关系

为了更好的进行相关方法的封装处理,我们把一些常规的接口处理放在BaseApiController里面,而把基于业务表的操作接口放在BusinessController里面定义,如下所示。

在BaseApiController里面,我们使用了结果封装和异常处理的过滤器统一处理,以便简化代码,如下控制器类定义。

    /// <summary>
    ///所有接口基类/// </summary>
[ExceptionHandling]
[WrapResult]
public class BaseApiController : ApiController

其中ExceptionHandling 和WrapResult的过滤器处理,可以参考我的随笔《
利用过滤器Filter和特性Attribute实现对Web API返回结果的封装和统一异常处理
》进行详细了解。

而业务类的接口通用封装,则放在了BusinessController控制器里面,其中使用了泛型定义,包括实体类,业务操作类,分页条件类等内容作为约束参数,如下所示。

    /// <summary>
    ///本控制器基类专门为访问数据业务对象而设的基类/// </summary>
    /// <typeparam name="B">业务对象类型</typeparam>
    /// <typeparam name="T">实体类类型</typeparam>
[ApiAuthorize]public class BusinessController<B, T, TGetAllInput>: BaseApiControllerwhere B : class
        whereTGetAllInput : IPagedAndSortedResultRequestwhere T : BaseEntity, new()

2、分页处理接口

其中IPagedAndSortedResultRequest接口,是借鉴ABP框架中对于分页部分的处理,因此分页函数需要实现这个接口,这个接口包含了请求的数量,偏移量, 以及排序等属性定义的。

而BusinessController的分页查询处理函数GetAll定义如下所示。

        /// <summary>
        ///分页获取记录/// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
[HttpGet]public virtual PagedResultDto<T>GetAll([FromUri] TGetAllInput input)
{
var condition =GetCondition(input);var list =GetPagedData(condition, input);returnlist;
}

其中 GetCondition 函数是给子类进行重写,以便处理不同的条件查询的。我们以UserController控制器为例进行说明。

    /// <summary>
    ///用户信息的业务控制器/// </summary>
    public class UserController : BusinessController<User, UserInfo, UserPagedDto>

其中传入的User是BLL业务层类,用来操作数据库;UserInfo是实体类,用来传递记录信息;UserPagedDto 则是分页查询条件类。

    /// <summary>
    ///用户信息的业务查询类/// </summary>
    public classUserPagedDto : PagedAndSortedInputDto, IPagedAndSortedResultRequest
{
/// <summary> ///默认构造函数/// </summary> public UserPagedDto() : base() { }/// <summary> ///参数化构造函数/// </summary> /// <param name="skipCount">跳过的数量</param> /// <param name="resultCount">最大结果集数量</param> public UserPagedDto(int skipCount, int resultCount) : base(skipCount, resultCount)
{
}
/// <summary> ///使用分页信息进行初始化SkipCount 和 MaxResultCount/// </summary> /// <param name="pagerInfo">分页信息</param> public UserPagedDto(PagerInfo pagerInfo) : base(pagerInfo)
{
}
#region Property Members /// <summary> ///所属角色ID/// </summary> public virtual int? Role_ID { get; set; }public virtual int? ID { get; set; }/// <summary> ///用户编码/// </summary> public virtual string HandNo { get; set; }/// <summary> ///用户名/登录名/// </summary> public virtual string Name { get; set; }/// <summary> ///用户密码/// </summary> public virtual string Password { get; set; }/// <summary> ///用户全名/// </summary> public virtual string FullName { get; set; }/// <summary> ///移动电话/// </summary> public virtual string MobilePhone { get; set; }/// <summary> ///邮件地址/// </summary> public virtual string Email { get; set; }/// <summary> ///默认部门ID/// </summary> public virtual string Dept_ID { get; set; }/// <summary> ///所属机构ID/// </summary> public virtual string Company_ID { get; set; }/// <summary> ///父ID/// </summary> public virtual int? PID { get; set; }/// <summary> ///用户呢称/// </summary> public virtual string Nickname { get; set; }/// <summary> ///是否过期/// </summary> public virtual bool? IsExpire { get; set; }/// <summary> ///过期日期/// </summary> public virtual DateTime? ExpireDateStart { get; set; }public virtual DateTime? ExpireDateEnd { get; set; }/// <summary> ///职务头衔/// </summary> public virtual string Title { get; set; }/// <summary> ///身份证号码/// </summary> public virtual string IdentityCard { get; set; }/// <summary> ///办公电话/// </summary> public virtual string OfficePhone { get; set; }/// <summary> ///家庭电话/// </summary> public virtual string HomePhone { get; set; }/// <summary> ///住址/// </summary> public virtual string Address { get; set; }/// <summary> ///办公地址/// </summary> public virtual string WorkAddr { get; set; }/// <summary> ///性别/// </summary> public virtual string Gender { get; set; }/// <summary> ///出生日期/// </summary> public virtual DateTime? BirthdayStart { get; set; }public virtual DateTime? BirthdayEnd { get; set; }/// <summary> ///QQ号码/// </summary> public virtual string QQ { get; set; }/// <summary> ///个性签名/// </summary> public virtual string Signature { get; set; }/// <summary> ///审核状态/// </summary> public virtual string AuditStatus { get; set; }/// <summary> ///备注/// </summary> public virtual string Note { get; set; }/// <summary> ///自定义字段/// </summary> public virtual string CustomField { get; set; }/// <summary> ///默认部门名称/// </summary> public virtual string DeptName { get; set; }/// <summary> ///所属机构名称/// </summary> public virtual string CompanyName { get; set; }/// <summary> ///排序码/// </summary> public virtual string SortCode { get; set; }/// <summary> ///创建人/// </summary> public virtual string Creator { get; set; }/// <summary> ///创建人ID/// </summary> public virtual string Creator_ID { get; set; }/// <summary> ///创建时间/// </summary> public virtual DateTime? CreateTimeStart { get; set; }public virtual DateTime? CreateTimeEnd { get; set; }/// <summary> ///编辑人/// </summary> public virtual string Editor { get; set; }/// <summary> ///编辑人ID/// </summary> public virtual string Editor_ID { get; set; }/// <summary> ///编辑时间/// </summary> public virtual DateTime? EditTimeStart { get; set; }public virtual DateTime? EditTimeEnd { get; set; }/// <summary> ///是否已删除/// </summary> public virtual bool? Deleted { get; set; }/// <summary> ///当前登录IP/// </summary> public virtual string CurrentLoginIP { get; set; }/// <summary> ///当前登录时间/// </summary> public virtual DateTime CurrentLoginTime { get; set; }/// <summary> ///当前Mac地址/// </summary> public virtual string CurrentMacAddress { get; set; }/// <summary> ///微信绑定的OpenId/// </summary> public virtual string OpenId { get; set; }/// <summary> ///微信多平台应用下的统一ID/// </summary> public virtual string UnionId { get; set; }/// <summary> ///公众号状态/// </summary> public virtual string Status { get; set; }/// <summary> ///公众号/// </summary> public virtual string SubscribeWechat { get; set; }/// <summary> ///科室权限/// </summary> public virtual string DeptPermission { get; set; }/// <summary> ///企业微信UserID/// </summary> public virtual string CorpUserId { get; set; }/// <summary> ///企业微信状态/// </summary> public virtual string CorpStatus { get; set; }#endregion}

它的基类属性包括了MaxResultCount,SkipCount,Sorting等分页排序所需的信息。

另外还包含了对条件查询的属性信息,如果是数值的,布尔类型的,则是可空类型,日期则有起始条件的范围属性等等,也可以根据自己需要定义更多属性用户过滤条件。

如对于出生日期,我们定义一个区间范围来进行查询。

        /// <summary>
        ///出生日期/// </summary>
        public virtual DateTime? BirthdayStart { get; set; }public virtual DateTime? BirthdayEnd { get; set; }

最后,我们根据需要进行判断,获得查询条件即可。

        /// <summary>
        ///获取查询条件并转换为SQL/// </summary>
        /// <param name="input">查询条件</param>
        protected override stringGetCondition(UserPagedDto input)
{
//根据条件,构建SQL条件语句 SearchCondition condition = newSearchCondition();if (!input.Role_ID.HasValue)
{
condition.AddCondition(
"ID", input.ID, SqlOperator.Equal)
.AddCondition(
"IdentityCard", input.IdentityCard, SqlOperator.Equal)
.AddCondition(
"Name", input.Name, SqlOperator.Like)
.AddCondition(
"Note", input.Note, SqlOperator.Like)
.AddCondition(
"Email", input.Email, SqlOperator.Like)
.AddCondition(
"MobilePhone", input.MobilePhone, SqlOperator.Like)
.AddCondition(
"Address", input.Address, SqlOperator.Like)
.AddCondition(
"HandNo", input.HandNo, SqlOperator.Like)
.AddCondition(
"HomePhone", input.HomePhone, SqlOperator.Like)
.AddCondition(
"Nickname", input.Nickname, SqlOperator.Like)
.AddCondition(
"OfficePhone", input.OfficePhone, SqlOperator.Like)
.AddCondition(
"OpenId", input.OpenId, SqlOperator.Like)
.AddCondition(
"Password", input.Password, SqlOperator.Like)
.AddCondition(
"PID", input.PID, SqlOperator.Like)
.AddCondition(
"QQ", input.QQ, SqlOperator.Equal)
.AddCondition(
"DeptPermission", input.DeptPermission, SqlOperator.Like)
.AddCondition(
"AuditStatus", input.AuditStatus, SqlOperator.Equal)
.AddCondition(
"FullName", input.FullName, SqlOperator.Like)
.AddCondition(
"Gender", input.Gender, SqlOperator.Equal)
.AddCondition(
"CustomField", input.CustomField, SqlOperator.Like)
.AddCondition(
"IsExpire", input.IsExpire, SqlOperator.Equal)
.AddCondition(
"Signature", input.Signature, SqlOperator.Like)
.AddCondition(
"SortCode", input.SortCode, SqlOperator.Like)
.AddCondition(
"Status", input.Status, SqlOperator.Equal)
.AddCondition(
"CorpStatus", input.CorpStatus, SqlOperator.Equal)
.AddCondition(
"CorpUserId", input.CorpUserId, SqlOperator.Equal)
.AddCondition(
"UnionId", input.UnionId, SqlOperator.Equal)
.AddCondition(
"WorkAddr", input.WorkAddr, SqlOperator.Equal)
.AddCondition(
"SubscribeWechat", input.SubscribeWechat, SqlOperator.Equal)
.AddCondition(
"Title", input.Title, SqlOperator.Like)
.AddCondition(
"CurrentLoginIP", input.CurrentLoginIP, SqlOperator.Like)
.AddCondition(
"CurrentMacAddress", input.CurrentMacAddress, SqlOperator.Like)

.AddCondition(
"Dept_ID", input.Dept_ID, SqlOperator.Equal)
.AddCondition(
"DeptName", input.DeptName, SqlOperator.Like)
.AddCondition(
"CompanyName", input.CompanyName, SqlOperator.Like)
.AddCondition(
"Company_ID", input.Company_ID, SqlOperator.Equal)
.AddCondition(
"Editor_ID", input.Editor_ID, SqlOperator.Equal)
.AddCondition(
"Editor", input.Editor, SqlOperator.Equal)
.AddCondition(
"Creator_ID", input.Creator_ID, SqlOperator.Equal)
.AddCondition(
"Creator", input.Creator, SqlOperator.Equal)

.AddDateCondition(
"CreateTime", input.CreateTimeStart, input.CreateTimeEnd)
.AddDateCondition(
"EditTime", input.EditTimeStart, input.EditTimeEnd)
.AddDateCondition(
"ExpireDate", input.ExpireDateStart, input.ExpireDateEnd)
.AddDateCondition(
"Birthday", input.BirthdayStart, input.BirthdayEnd);
}
return condition.BuildConditionSql().Replace("Where", "");
}

前面介绍到,我们BusinessController基类定义了常规的分页查询GetAll函数,如下所示。

        /// <summary>
        ///分页获取记录/// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
[HttpGet]public virtual PagedResultDto<T>GetAll([FromUri] TGetAllInput input)
{
var condition =GetCondition(input);var list =GetPagedData(condition, input);returnlist;
}

其中 GetCondition 是由子类进行重写处理,生成具体的查询条件的。

由于这里的Sorting信息是一个字符串的排序信息,如 Name DESC或者Name ASC类似的信息,前者是字段名,后者是排序降序还是升序的标识,我们在业务里面,需要拆分一下进行组合条件,如下拆分。

            //分页查询条件
            string sortName = null; //排序字段
            bool isDesc = true;if (!string.IsNullOrEmpty(input.Sorting))
{
var sortInput = input asISortedResultRequest;if (sortInput != null)
{
if (!string.IsNullOrWhiteSpace(sortInput.Sorting))
{
List
<string> strNames = sortInput.Sorting.ToDelimitedList<string>(" ");
sortName
= (strNames.Count > 0) ? strNames[0] : null;
isDesc
= sortInput.Sorting.IndexOf("desc", StringComparison.OrdinalIgnoreCase) > 0;
}
}
}

这样我们或者SortName,以及是否降序的判断。

然后根据获得分页信息,并调用业务类的接口函数获取对应记录,构建为分页所需的JSON对象返回。

            //构建分页对象
            var pagerInfo = new PagerInfo() { CurrenetPageIndex = currentPage, PageSize =pageSize };if (!string.IsNullOrWhiteSpace(sortName))
{
list
=baseBLL.FindWithPager(condition, pagerInfo, sortName, isDesc);
}
else{
list
=baseBLL.FindWithPager(condition, pagerInfo);
}
if (list != null)
{
foreach (var item inlist)
{
ConvertDto(item);
//对Dto部分内容进行转义 }
}
//返回常用分页对象 var result = new PagedResultDto<T> { TotalCount = totalCount, Items =list };return result;

其中 PagedResultDto 是一个标准的分页数据返回的对象,定义如下所示。

[Serializable]public class PagedResultDto<T> : ListResultDto<T>, IPagedResult<T>{/// <summary>
        ///Total count of Items./// </summary>
        public int TotalCount { get; set; }
[Serializable]public class ListResultDto<T> : IListResult<T>{/// <summary>
        ///List of items./// </summary>
        public IReadOnlyList<T>Items
{
get { return _items ?? (_items = new List<T>()); }set { _items =value; }
}
private IReadOnlyList<T> _items;

最后返回的结果集合类似如下所示:

展开单条记录明细如下所示。

这个对象使用了Camel样式的属性处理,所以返回的属性全部是Camel的格式。

    /// <summary>
    ///统一处理Json的格式化信息/// </summary>
    public static classJsonFomatterHelper
{
/// <summary> ///获取JSON的格式化信息/// </summary> /// <returns></returns> public staticJsonMediaTypeFormatter GetFormatter()
{
var formatter =GlobalConfiguration.Configuration.Formatters.JsonFormatter;
formatter.SerializerSettings
= newJsonSerializerSettings
{
Formatting
=Formatting.Indented,
ContractResolver
= newCamelCasePropertyNamesContractResolver(),
DateFormatHandling
=DateFormatHandling.IsoDateFormat,
DateFormatString
= "yyyy-MM-dd HH:mm:ss",
};
returnformatter;
}
}

关于统一结果返回的封装处理,这里采用了WrapResultAttribute进行处理,详细可以参考我的随笔《
利用过滤器Filter和特性Attribute实现对Web API返回结果的封装和统一异常处理
》进行详细了解。

            //重新封装回传格式
            actionExecutedContext.Response = newHttpResponseMessage(statusCode)
{
Content
= new ObjectContent<AjaxResponse>(newAjaxResponse(content), JsonFomatterHelper.GetFormatter())
};

标签: none

添加新评论