文章目录
概述
NopCommerce源码架构详解-数据分页相关源码分析。
内容
nop是采用的IPagedList 泛型分页的。今天我们就来看看它是如果实现的?
1、Nop.Core.IPagedList
首先Nop定义了一个使用泛型集合的分页接口IPagedList:
using System.Collections.Generic; namespace Nop.Core { /// <summary> /// Paged list interface /// </summary> public interface IPagedList<T> : IList<T> { int PageIndex { get; } int PageSize { get; } int TotalCount { get; } int TotalPages { get; } bool HasPreviousPage { get; } bool HasNextPage { get; } } }
2、Nop.Core.PagedList
具体的接口IPagedList实现类PagedList:
using System; using System.Collections.Generic; using System.linq; namespace Nop.Core { /// <summary> /// Paged list /// </summary> /// <typeparam name="T">T</typeparam> [Serializable] public class PagedList<T> : List<T>, IPagedList<T> { /// <summary> /// Ctor /// </summary> /// <param name="source">数据源</param> /// <param name="pageIndex">页码</param> /// <param name="pageSize">每页的大小</param> public PagedList(IQueryable<T> source, int pageIndex, int pageSize) { int total = source.Count(); this.TotalCount = total; this.TotalPages = total / pageSize; if (total % pageSize > 0) TotalPages++; this.PageSize = pageSize; this.PageIndex = pageIndex; this.AddRange(source.Skip(pageIndex * pageSize).Take(pageSize).ToList()); } /// <summary> /// Ctor /// </summary> /// <param name="source">数据源</param> /// <param name="pageIndex">页码</param> /// <param name="pageSize">每页的大小</param> public PagedList(IList<T> source, int pageIndex, int pageSize) { TotalCount = source.Count(); TotalPages = TotalCount / pageSize; if (TotalCount % pageSize > 0) TotalPages++; this.PageSize = pageSize; this.PageIndex = pageIndex; this.AddRange(source.Skip(pageIndex * pageSize).Take(pageSize).ToList()); } /// <summary> /// Ctor /// </summary> /// <param name="source">数据源</param> /// <param name="pageIndex">页码</param> /// <param name="pageSize">每页的大小</param> /// <param name="totalCount">总记录数</param> public PagedList(IEnumerable<T> source, int pageIndex, int pageSize, int totalCount) { TotalCount = totalCount; TotalPages = TotalCount / pageSize; if (TotalCount % pageSize > 0) TotalPages++; this.PageSize = pageSize; this.PageIndex = pageIndex; this.AddRange(source); } public int PageIndex { get; private set; } public int PageSize { get; private set; } public int TotalCount { get; private set; } public int TotalPages { get; private set; } public bool HasPreviousPage { get { return (PageIndex > 0); }//是否有前一页 } public bool HasNextPage { get { return (PageIndex + 1 < TotalPages); }//是否有下一页 } } }
注意:pageIndex是从0开始的,也就是第一页pageIndex为0,而第二页pageIndex为1。
3、Nop.Core.PagedList在Service中的使用
我们来看看Nop.Core.PagedList在Nop.Services.Catalog中GetProductCategoriesByCategoryId方法是如何调用的分页:
/// <summary> /// 通过指定产品分类下面的产品分页数据 /// </summary> /// <param name="categoryId">分类ID</param> /// <param name="pageIndex">页码</param> /// <param name="pageSize">每页的大小</param> /// <param name="showHidden"></param> /// <returns>返回满足条件的产品</returns> public virtual IPagedList<ProductCategory> GetProductCategoriesByCategoryId(int categoryId, int pageIndex, int pageSize, bool showHidden = false) { //返回一个空的PagedList集合 if (categoryId == 0) return new PagedList<ProductCategory>(new List<ProductCategory>(), pageIndex, pageSize); string key = string.Format(PRODUCTCATEGORIES_ALLBYCATEGORYID_KEY, showHidden, categoryId, pageIndex, pageSize, _workContext.CurrentCustomer.Id, _storeContext.CurrentStore.Id); return _cacheManager.Get(key, () => { var query = from pc in _productCategoryRepository.Table join p in _productRepository.Table on pc.ProductId equals p.Id where pc.CategoryId == categoryId && !p.Deleted && (showHidden || p.Published) orderby pc.DisplayOrder select pc; if (!showHidden && (!_catalogSettings.IgnoreAcl || !_catalogSettings.IgnoreStoreLimitations)) { if (!_catalogSettings.IgnoreAcl) { //ACL (access control list) var allowedCustomerRolesIds = _workContext.CurrentCustomer.CustomerRoles .Where(cr => cr.Active).Select(cr => cr.Id).ToList(); query = from pc in query join c in _categoryRepository.Table on pc.CategoryId equals c.Id join acl in _aclRepository.Table on new { c1 = c.Id, c2 = "Category" } equals new { c1 = acl.EntityId, c2 = acl.EntityName } into c_acl from acl in c_acl.DefaultIfEmpty() where !c.SubjectToAcl || allowedCustomerRolesIds.Contains(acl.CustomerRoleId) select pc; } if (!_catalogSettings.IgnoreStoreLimitations) { //Store mapping var currentStoreId = _storeContext.CurrentStore.Id; query = from pc in query join c in _categoryRepository.Table on pc.CategoryId equals c.Id join sm in _storeMappingRepository.Table on new { c1 = c.Id, c2 = "Category" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into c_sm from sm in c_sm.DefaultIfEmpty() where !c.LimitedToStores || currentStoreId == sm.StoreId select pc; } //only distinct categories (group by ID) query = from c in query group c by c.Id into cGroup orderby cGroup.Key select cGroup.FirstOrDefault(); query = query.OrderBy(pc => pc.DisplayOrder); } //调用PagedList的构造函数返回一个分页之后的IPagedList实例 var productCategories = new PagedList<ProductCategory>(query, pageIndex, pageSize); return productCategories; }); }
可以看到上面看到Nop的分页流程大概是这样的:通过筛选数据然后把满足条件的数据源传给PageList,然后通过在PageList里面通过Linq的Skip和Take进行分页,最后只返回指定页码的数据。