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

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

在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())
            };

 

专注于代码生成工具、.Net/.NetCore 框架架构及软件开发,以及各种Vue.js的前端技术应用。著有Winform开发框架/混合式开发框架、微信开发框架、Bootstrap开发框架、ABP开发框架、SqlSugar开发框架等框架产品。
 转载请注明出处:撰写人:伍华聪  http://www.iqidi.com

相关文章
|
28天前
|
前端开发 开发者
new操作符背后的秘密:揭开Web前端对象创建的神秘面纱!
【8月更文挑战第23天】在Web前端开发中,`new`操作符是创建对象实例的核心。本文以`Person`构造函数为例,通过四个步骤解析`new`操作符的工作原理:创建空对象、设置新对象原型、执行构造函数并调整`this`指向、判断并返回最终对象。了解这些有助于开发者更好地理解对象实例化过程,从而编写出更规范、易维护的代码。
25 0
|
1月前
|
JSON 前端开发 JavaScript
|
20天前
|
安全 关系型数据库 数据库
FastAPI数据库操作秘籍:如何通过高效且安全的数据库访问策略,使你的Web应用飞速运转并保持数据完整性?
【8月更文挑战第31天】在构建现代Web应用时,数据库操作至关重要。FastAPI不仅简化了API创建,还提供了高效数据库交互的方法。本文探讨如何在FastAPI中实现快速、安全的数据处理。FastAPI支持多种数据库,如SQLite、PostgreSQL和MySQL;选择合适的数据库可显著提升性能。通过安装相应驱动并配置连接参数,结合ORM库(如Tortoise-ORM或SQLAlchemy),可以简化数据库操作。使用索引、批量操作及异步处理等最佳实践可进一步提高效率。同时,确保使用参数化查询防止SQL注入,并从环境变量中读取敏感信息以增强安全性。
39 1
|
23天前
|
数据采集 定位技术 API
为什么要选住宅IP代理抓取Web数据?
在数据采集和Web爬虫领域,选择合适的IP代理至关重要。住宅IP代理因高隐蔽性和真实性、更好的访问成功率、能绕过反爬虫策略、多样化的地理位置、适应性和灵活性以及合法合规等优势,成为许多数据抓取项目的首选。使用住宅IP代理可提高数据抓取效率,降低被封禁风险,同时需遵守法律法规,确保活动的合法性与合规性。
|
27天前
|
存储 缓存 监控
Memcached玩转Web性能:一致性哈希、数据持久化,一文全掌握!
【8月更文挑战第24天】Memcached是一款高性能的分布式内存对象缓存系统,它通过在网络中存储数据并使用简单的键值对机制来提高动态Web应用的性能。它可以显著减少数据库查询次数,进而减轻数据库负载并加快响应时间。为了最大化利用Memcached的优势,建议合理配置内存使用、采用一致性哈希策略、实施数据持久化措施,并持续监控系统健康状况。提供的示例代码展示了如何使用Java创建客户端、添加和获取数据。
30 1
|
29天前
|
XML JavaScript 测试技术
Web自动化测试框架(基础篇)--HTML页面元素和DOM对象
本文为Web自动化测试入门指南,介绍了HTML页面元素和DOM对象的基础知识,以及如何使用Python中的Selenium WebDriver进行元素定位、操作和等待机制,旨在帮助初学者理解Web自动化测试中的关键概念和操作技巧。
36 1
|
20天前
|
物联网 C# 智能硬件
智能家居新篇章:WPF与物联网的智慧碰撞——通过MQTT协议连接与控制智能设备,打造现代科技生活的完美体验
【8月更文挑战第31天】物联网(IoT)技术的发展使智能家居设备成为现代家庭的一部分。通过物联网,家用电器和传感器可以互联互通,实现远程控制和状态监测等功能。本文将探讨如何在Windows Presentation Foundation(WPF)应用中集成物联网技术,通过具体示例代码展示其实现过程。文章首先介绍了MQTT协议及其在智能家居中的应用,并详细描述了使用Wi-Fi连接方式的原因。随后,通过安装Paho MQTT客户端库并创建MQTT客户端实例,演示了如何编写一个简单的WPF应用程序来控制智能灯泡。
40 0
|
20天前
|
Java 数据库 API
JSF与JPA的史诗级联盟:如何编织数据持久化的华丽织锦,重塑Web应用的荣耀
【8月更文挑战第31天】JavaServer Faces (JSF) 和 Java Persistence API (JPA) 分别是构建Java Web应用的用户界面组件框架和持久化标准。结合使用JSF与JPA,能够打造强大的数据驱动Web应用。首先,通过定义实体类(如`User`)和配置`persistence.xml`来设置JPA环境。然后,在JSF中利用Managed Bean(如`UserBean`)管理业务逻辑,通过`EntityManager`执行数据持久化操作。
31 0
|
20天前
|
Java 开发者 关系型数据库
JSF与AWS的神秘之旅:如何在云端部署JSF应用,让你的Web应用如虎添翼?
【8月更文挑战第31天】在云计算蓬勃发展的今天,AWS已成为企业级应用的首选平台。本文探讨了在AWS上部署JSF(JavaServer Faces)应用的方法,这是一种广泛使用的Java Web框架。通过了解并利用AWS的基础设施与服务,如EC2、RDS 和 S3,开发者能够高效地部署和管理JSF应用。文章还提供了具体的部署步骤示例,并讨论了使用AWS可能遇到的挑战及应对策略,帮助开发者更好地利用AWS的强大功能,提升Web应用开发效率。
42 0
|
20天前
|
开发者 Java Spring
【绝技揭秘】掌握Vaadin数据绑定:一键同步Java对象,告别手动数据烦恼,轻松玩转Web应用开发!
【8月更文挑战第31天】Vaadin不仅是一个功能丰富的Java Web应用框架,还提供了强大的数据绑定机制,使开发者能轻松连接UI组件与后端Java对象,简化Web应用开发流程。本文通过创建一个简单的用户信息表单示例,详细介绍了如何使用Vaadin的`Binder`类实现数据绑定,包括字段与模型属性的双向绑定及数据验证。通过这个示例,开发者可以更专注于业务逻辑而非繁琐的数据同步工作,提高开发效率和应用可维护性。
41 0