[c#] 개체에 대한 LINQ를 사용한 페이징

LINQ 쿼리에서 페이징을 어떻게 구현 하시겠습니까? 사실 당분간은 sql TOP 함수를 모방 할 수 있다면 만족할 것입니다. 그러나 나는 완전한 페이징 지원에 대한 필요성이 어차피 조만간 나올 것이라고 확신합니다.

var queryResult = from o in objects
                  where ...
                  select new
                      {
                         A = o.a,
                         B = o.b
                      }
                   ????????? TOP 10????????



답변

SkipTake확장 방법을 찾고 있습니다. Skip결과에서 처음 N 개 요소를지나 이동하여 나머지를 반환합니다. Take결과의 처음 N 개 요소를 반환하고 나머지 요소를 모두 삭제합니다.

이러한 방법을 사용하는 방법에 대한 자세한 내용은 MSDN을 참조하십시오. http://msdn.microsoft.com/en-us/library/bb386988.aspx

pageNumber가 0에서 시작해야한다는 것을 이미 고려하고 있다고 가정하면 (댓글에서 제안한대로 1 당 감소) 다음과 같이 할 수 있습니다.

int numberOfObjectsPerPage = 10;
var queryResultPage = queryResult
  .Skip(numberOfObjectsPerPage * pageNumber)
  .Take(numberOfObjectsPerPage);

그렇지 않으면 @Alvin이 제안한대로

int numberOfObjectsPerPage = 10;
var queryResultPage = queryResult
  .Skip(numberOfObjectsPerPage * (pageNumber - 1))
  .Take(numberOfObjectsPerPage);


답변

Skip및 사용 Take은 확실히 갈 길입니다. 이것을 구현한다면 아마도 페이징을 처리하는 (코드를 더 읽기 쉽게 만들기 위해) 나만의 확장 메서드를 작성할 것입니다. 물론 사용의 구현 캔 SkipTake:

static class PagingUtils {
  public static IEnumerable<T> Page<T>(this IEnumerable<T> en, int pageSize, int page) {
    return en.Skip(page * pageSize).Take(pageSize);
  }
  public static IQueryable<T> Page<T>(this IQueryable<T> en, int pageSize, int page) {
    return en.Skip(page * pageSize).Take(pageSize);
  }
}

하나 – 클래스는 두 개의 확장 메서드 정의 IEnumerable및 하나 IQueryable, 당신은 SQL에 개체 및 LINQ (데이터베이스 쿼리를 작성할 때, 컴파일러는 선택할 것까지 모두 LINQ와 함께 사용할 수있는 수단을 IQueryable버전).

페이징 요구 사항에 따라 몇 가지 추가 동작 (예 : 음수 pageSize또는 page값 처리)을 추가 할 수도 있습니다 . 다음은 쿼리에서이 확장 메서드를 사용하는 방법의 예입니다.

var q = (from p in products
         where p.Show == true
         select new { p.Name }).Page(10, pageIndex);


답변

다음은 LINQ to 개체를 사용할 때 페이징에 대한 내 성능 접근 방식입니다.

public static IEnumerable<IEnumerable<T>> Page<T>(this IEnumerable<T> source, int pageSize)
{
    Contract.Requires(source != null);
    Contract.Requires(pageSize > 0);
    Contract.Ensures(Contract.Result<IEnumerable<IEnumerable<T>>>() != null);

    using (var enumerator = source.GetEnumerator())
    {
        while (enumerator.MoveNext())
        {
            var currentPage = new List<T>(pageSize)
            {
                enumerator.Current
            };

            while (currentPage.Count < pageSize && enumerator.MoveNext())
            {
                currentPage.Add(enumerator.Current);
            }
            yield return new ReadOnlyCollection<T>(currentPage);
        }
    }
}

그러면 다음과 같이 사용할 수 있습니다.

var items = Enumerable.Range(0, 12);

foreach(var page in items.Page(3))
{
    // Do something with each page
    foreach(var item in page)
    {
        // Do something with the item in the current page       
    }
}

이 쓰레기의 없음 SkipTake여러 페이지에 관심이 있다면 이는 매우 비효율적 일 수 없습니다.


답변

   ( for o in objects
    where ...
    select new
   {
     A=o.a,
     B=o.b
   })
.Skip((page-1)*pageSize)
.Take(pageSize)


답변

이것이 누구에게도 도움이 될지 모르겠지만 내 목적에 유용하다는 것을 알았습니다.

private static IEnumerable<T> PagedIterator<T>(IEnumerable<T> objectList, int PageSize)
{
    var page = 0;
    var recordCount = objectList.Count();
    var pageCount = (int)((recordCount + PageSize)/PageSize);

    if (recordCount < 1)
    {
        yield break;
    }

    while (page < pageCount)
    {
        var pageData = objectList.Skip(PageSize*page).Take(PageSize).ToList();

        foreach (var rd in pageData)
        {
            yield return rd;
        }
        page++;
    }
}

이를 사용하려면 linq 쿼리가 있고 페이지 크기와 함께 결과를 foreach 루프에 전달합니다.

var results = from a in dbContext.Authors
              where a.PublishDate > someDate
              orderby a.Publisher
              select a;

foreach(var author in PagedIterator(results, 100))
{
    // Do Stuff
}

따라서 이것은 한 번에 100 명의 작성자를 가져 오는 각 작성자에 대해 반복됩니다.


답변

편집-필요하지 않으므로 Skip (0) 제거

var queryResult = (from o in objects where ...
                      select new
                      {
                          A = o.a,
                          B = o.b
                      }
                  ).Take(10);


답변

var pages = items.Select((item, index) => new { item, Page = index / batchSize }).GroupBy(g => g.Page);

Batchsize는 분명히 정수입니다. 이것은 정수가 단순히 소수 자리를 삭제한다는 사실을 활용합니다.

나는이 응답에 대해 반 농담이지만 ​​원하는대로 수행 할 것이며 지연되기 때문에 그렇게해도 큰 성능 저하를 초래하지 않을 것입니다.

pages.First(p => p.Key == thePage)

이 솔루션은 LinqToEntities를위한 것이 아니며 이것이 좋은 쿼리로 바뀔 수 있는지조차 모릅니다.