6

利用查询条件对象,在Asp.net Web API中实现对业务数据的分页查询处理

 3 years ago
source link: https://www.cnblogs.com/wuhuacong/p/14675938.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在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> : BaseApiController
        where B : class
        where TGetAllInput : IPagedAndSortedResultRequest
        where 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);
            return list;
        }

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

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

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

    /// <summary>
    /// 用户信息的业务查询类
    /// </summary>
    public class UserPagedDto : 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 string GetCondition(UserPagedDto input)
        {
            //根据条件,构建SQL条件语句
            SearchCondition condition = new SearchCondition();
            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);
            return list;
        }

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

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

            //分页查询条件
            string sortName = null; //排序字段
            bool isDesc = true;
            if (!string.IsNullOrEmpty(input.Sorting))
            {
                var sortInput = input as ISortedResultRequest;
                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 in list)
                {
                    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 class JsonFomatterHelper
    {
        /// <summary>
        /// 获取JSON的格式化信息
        /// </summary>
        /// <returns></returns>
        public static JsonMediaTypeFormatter GetFormatter()
        {
            var formatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
            formatter.SerializerSettings = new JsonSerializerSettings
            {
                Formatting = Formatting.Indented,
                ContractResolver = new CamelCasePropertyNamesContractResolver(),
                DateFormatHandling = DateFormatHandling.IsoDateFormat,
                DateFormatString = "yyyy-MM-dd HH:mm:ss",
            };
            return formatter;
        }
    }

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

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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK