简体   繁体   English

使用 IList IEnumerable 存储库实现分页列表

[英]Implement Paged List with IList IEnumerable Repository

I inherited a project, and asked to implemented Paging across all our Repositories (500+ methods), to show only segments of data on web front end.我继承了一个项目,并要求在我们所有的存储库中实现分页(500 多种方法),以仅显示 web 前端的数据段。

Current Repository is below as following.当前存储库如下。 They return Task Lists, IEnumerables etc.它们返回任务列表、IEnumerables 等。

How would we go through, and implement paging for the following Repositories examples?我们将如何通过 go 并为以下存储库示例实现分页?

If I implement following Paged List answer, the repos return IList, not IQueryable.如果我实现以下分页列表答案,则存储库返回 IList,而不是 IQueryable。 So the answer, would still Retrieve All the Data first (causing performance issue), and then filter it.所以答案仍然是首先检索所有数据(导致性能问题),然后过滤它。

https://stackoverflow.com/a/5036023/12425844 https://stackoverflow.com/a/5036023/12425844

Repository:存储库:

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;
}

Page List Answer页面列表答案

public PagedList<T> Find(Expression<Func<T,bool>> predicate, int pageNumber, pageSize)
{
   return repository
             .Find()
             .Where(predicate)
             .ToPagedList(pageNumber, pageSize);
}

*Looking for an efficient way, so don't have to Recopy and Paste All the 500+ methods with paging,if possible *寻找一种有效的方法,所以如果可能的话,不必重新复制和粘贴所有 500 多种分页方法

If changing all the Repos methods to IQueryable, that may fix the issue.如果将所有 Repos 方法更改为 IQueryable,则可能会解决问题。 However, was reading Repos should Not return IQueryable per article here Entity Framework Repository Pattern why not return Iqueryable?但是,阅读 Repos should Not return IQueryable per article here Entity Framework Repository Pattern 为什么不返回 Iqueryable?

If you want to return exactly the data for a page you can use LINQ.Skip and.Take, that will still return a IQueryable如果你想准确返回一个页面的数据,你可以使用 LINQ.Skip and.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;
}

The default of this approach is that if new data appears regularly, your pages can lose synchronisation.这种方法的默认设置是,如果定期出现新数据,您的页面可能会失去同步。 For example, your query page 2, 25 items and get 25-49.例如,您查询第 2 页,25 个项目,得到 25-49。 Then 2 new items are inserted and you query page 3, you will get what used to be 48-72 But this is a standard behaviour on most web sites.然后插入 2 个新项目并查询第 3 页,您将得到以前的 48-72 但这是大多数 web 站点的标准行为。

Now to address the issue of DRY if you want to keep the existing queries, I would separate first the query itself from the transformation to an asynchronous list:现在要解决 DRY 问题,如果您想保留现有查询,我将首先将查询本身与转换为异步列表分开:

  private IQueryable<Product> GetProductListByProductNumberQuery(int bkProductNumber)
        {
            var productData = _context.Product
                .Include(c => c.LkProducttype)
                .Where(c => c.BkProductnumber == bkProductNumber);
            return productData;
        }

And then add the way to use the queryable as needed (example with extension methods, but can be done with a class to wrap, or in your repository with 2 ways of invoking a queryable):然后根据需要添加使用可查询的方式(使用扩展方法的示例,但可以使用 class 进行包装,或者在您的存储库中使用两种调用可查询的方式):

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

Differing the use of ToListAsync allow to come back to the query to refine it with paging ToListAsync 的不同使用允许返回查询以通过分页对其进行细化

Whether the queries are public or private will depend on how you want to use them.查询是公开的还是私有的将取决于您希望如何使用它们。 If you consider that the repository is responsible for providing the queries but not how to run them, then the queries will be public.如果您认为存储库负责提供查询而不是如何运行它们,那么查询将是公开的。 If you want your repository to only expose wrapped executed queries, then the queries will be private.如果您希望您的存储库仅公开包装的已执行查询,则查询将是私有的。 Good practice is to not return IQueryable, but that would run against the constraint you set that you do not want to create 2 versions of each query (paged, not paged), so with that constraint I would go for exposing the IQueryable.好的做法是不返回 IQueryable,但这会违反您设置的约束,即您不想为每个查询创建 2 个版本(分页,而不是分页),因此使用该约束,我将使用 go 来公开 IQueryable。 But the articles you link are right in warning that users can start doing insane things with it.但是您链接的文章是正确的,警告用户可以开始用它做疯狂的事情。 If it is exposed to your team only, I think it's fine as long as every one is aware that it is a technical debt.如果它只暴露给你的团队,我认为只要每个人都知道这是技术债务就可以了。 If it is in a public API, then you can't afford to fix it later.如果它位于公共 API 中,那么您以后无法修复它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM