使用 IList IEnumerable Repository 实现分页列表
Implement Paged List with IList IEnumerable Repository
我继承了一个项目,并要求在我们所有的存储库(500 多种方法)中实现分页,以便在 Web 前端仅显示数据段。
当前存储库如下。他们 return 任务列表、IEnumerables 等。
我们将如何完成并为以下存储库示例实现分页?
如果我实施以下分页列表答案,回购 return IList,而不是 IQueryable。所以答案,仍然会先检索所有数据(导致性能问题),然后过滤它。
存储库:
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
如果您想要return 一个页面的准确数据,您可以使用 LINQ .Skip 和 .Take,那仍然 return 一个 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;
}
然后根据需要添加使用可查询的方式(使用扩展方法的示例,但可以使用 class 包装来完成,或者在您的存储库中使用 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 使用允许返回到查询以使用分页优化它
查询是 public 还是私人查询取决于您希望如何使用它们。如果您认为存储库负责提供查询而不是如何 运行 它们,那么查询将是 public。如果您希望您的存储库仅公开包装的已执行查询,那么这些查询将是私有的。
好的做法是不要 return IQueryable,但这会 运行 违反您设置的约束,即您不想为每个查询创建 2 个版本(分页,不分页),所以有了这个约束我会去公开 IQueryable。但是你 link 的文章警告用户可以开始用它做疯狂的事情是正确的。如果它只暴露给你的团队,我认为只要每个人都意识到这是一种技术债务就可以了。
如果它在 public API 中,那么您以后无法修复它。
我继承了一个项目,并要求在我们所有的存储库(500 多种方法)中实现分页,以便在 Web 前端仅显示数据段。
当前存储库如下。他们 return 任务列表、IEnumerables 等。
我们将如何完成并为以下存储库示例实现分页?
如果我实施以下分页列表答案,回购 return IList,而不是 IQueryable。所以答案,仍然会先检索所有数据(导致性能问题),然后过滤它。
存储库:
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
如果您想要return 一个页面的准确数据,您可以使用 LINQ .Skip 和 .Take,那仍然 return 一个 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;
}
然后根据需要添加使用可查询的方式(使用扩展方法的示例,但可以使用 class 包装来完成,或者在您的存储库中使用 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 使用允许返回到查询以使用分页优化它
查询是 public 还是私人查询取决于您希望如何使用它们。如果您认为存储库负责提供查询而不是如何 运行 它们,那么查询将是 public。如果您希望您的存储库仅公开包装的已执行查询,那么这些查询将是私有的。 好的做法是不要 return IQueryable,但这会 运行 违反您设置的约束,即您不想为每个查询创建 2 个版本(分页,不分页),所以有了这个约束我会去公开 IQueryable。但是你 link 的文章警告用户可以开始用它做疯狂的事情是正确的。如果它只暴露给你的团队,我认为只要每个人都意识到这是一种技术债务就可以了。 如果它在 public API 中,那么您以后无法修复它。