c# - 使用 IList IEnumerable 存储库实现分页列表
问题描述
我继承了一个项目,并要求在我们所有的存储库(500 多种方法)中实现分页,以仅在 Web 前端显示数据段。
当前存储库如下。它们返回任务列表、IEnumerables 等。
我们将如何完成并为以下存储库示例实现分页?
如果我实现以下分页列表答案,则存储库返回 IList,而不是 IQueryable。所以答案仍然是首先检索所有数据(导致性能问题),然后过滤它。
https://stackoverflow.com/a/5036023/12425844
存储库:
public Task<List<Product>> GetProductListByProductNumber(int bkProductNumber)
{
var productData = _context.Product
.Include(c => c.LkProducttype)
.Where(c => c.BkProductnumber == bkProductNumber).ToListAsync();
return productData ;
}
public Task<List<Product>> GetProductListByProductDescription(string productDescription)
{
var productData = _context.Product
.Include(c => c.LkProducttype)
.Where(c => c.ProductDescription == productDescription).ToListAsync();
return productData;
}
页面列表答案
public PagedList<T> Find(Expression<Func<T,bool>> predicate, int pageNumber, pageSize)
{
return repository
.Find()
.Where(predicate)
.ToPagedList(pageNumber, pageSize);
}
*寻找一种有效的方法,所以如果可能的话,不必重新复制和粘贴所有 500 多种分页方法
如果将所有 Repos 方法更改为 IQueryable,则可能会解决问题。但是,阅读 Repos should Not return IQueryable per article here Entity Framework Repository Pattern 为什么不返回 Iqueryable?
解决方案
如果您想准确返回页面的数据,您可以使用 LINQ .Skip 和 .Take,它仍然会返回 IQueryable
public Task<List<Product>> GetProductListByProductNumberAsync(int bkProductNumber, int pageNumber, int pageSize)
{
var productData = _context.Product
.Include(c => c.LkProducttype)
.Where(c => c.BkProductnumber == bkProductNumber)
.Skip(pageSize * (pageNumber - 1))
.Take(pageSize)
.ToListAsync();
return productData;
}
这种方法的默认设置是,如果定期出现新数据,您的页面可能会失去同步。例如,您查询第 2 页,25 个项目,得到 25-49。然后插入 2 个新项目并查询第 3 页,您将得到以前的 48-72 但这是大多数网站的标准行为。
现在要解决 DRY 问题,如果您想保留现有查询,我将首先将查询本身与转换为异步列表分开:
private IQueryable<Product> GetProductListByProductNumberQuery(int bkProductNumber)
{
var productData = _context.Product
.Include(c => c.LkProducttype)
.Where(c => c.BkProductnumber == bkProductNumber);
return productData;
}
然后根据需要添加使用可查询的方式(使用扩展方法的示例,但可以使用要包装的类来完成,或者在您的存储库中使用 2 种调用可查询的方式):
public static class QueryableExtensions
{
public static Task<List<T>> ToTask<T>(this IQueryable<T> query)
{
return query.ToListAsync(); // might as well call directly .ToListAsync
}
public static Task<List<T>> ToTaskPaged<T>(this IQueryable<T> query, int pageNumber, int pageSize)
{
return query
.Skip(pageSize * (pageNumber-1))
.Take(pageSize)
.ToListAsync();
}
}
ToListAsync 的不同使用允许返回查询以通过分页对其进行细化
查询是公开的还是私有的将取决于您希望如何使用它们。如果您认为存储库负责提供查询而不是如何运行它们,那么查询将是公开的。如果您希望您的存储库仅公开包装的已执行查询,则查询将是私有的。好的做法是不返回 IQueryable,但这会违反您设置的约束,即您不想为每个查询创建 2 个版本(分页,而不是分页),因此使用该约束,我将公开 IQueryable。但是您链接的文章是正确的,警告用户可以开始用它做疯狂的事情。如果它只暴露给你的团队,我认为只要每个人都知道这是技术债务就可以了。如果它在公共 API 中,那么您以后无法修复它。
推荐阅读
- excel - 用数字系列替换或移动特定值
- r - 如何将弹出帮助文本添加到 RStudio 中用户创建的包中的函数?
- azure - 检索令牌、访问后端和 Graph API 的最佳实践
- c# - 我可以在安装项目而不是 Windows 窗体项目中创建安装程序类吗?
- mongodb - bs4,如何比较抓取数据并在网站上发送有关新产品的通知?
- flutter - Dart 中的动态切换案例
- python - Python基于列表的自动变量赋值
- javascript - Chart.js:我可以更改 chartjs-zoom-plugin 中的“OriginalOptions”变量吗?
- sql - 如何在postgis中进行查询以了解方形是否与圆形相交或没有postgress
- c++ - 如何更好地组织类的记录器 - 静态指针字段、全局变量、单例字段等?