개체에 대해 LINQ를 사용한 페이징
LINQ 쿼리에서 페이징을 어떻게 구현 하시겠습니까? 사실 당분간은 sql TOP 함수를 모방 할 수 있다면 만족할 것입니다. 그러나 나는 완전한 페이징 지원에 대한 필요성이 어차피 조만간 나올 것이라고 확신합니다.
var queryResult = from o in objects
where ...
select new
{
A = o.a,
B = o.b
}
????????? TOP 10????????
Skip
및 Take
확장 방법을 찾고 있습니다. Skip
결과에서 처음 N 개 요소를지나 이동하여 나머지를 반환합니다. Take
결과의 처음 N 개 요소를 반환하고 나머지 요소를 모두 삭제합니다.
이러한 방법을 사용하는 방법에 대한 자세한 내용은 MSDN을 참조하십시오. http://msdn.microsoft.com/en-us/library/bb386988.aspx
예를 들면 :
int numberOfObjectsPerPage = 10;
var queryResultPage = queryResult
.Skip(numberOfObjectsPerPage * pageNumber)
.Take(numberOfObjectsPerPage);
Skip
및 사용 Take
은 확실히 갈 길입니다. 이것을 구현한다면 아마도 페이징을 처리하는 (코드를 더 읽기 쉽게 만들기 위해) 나만의 확장 메서드를 작성할 것입니다. 물론 사용의 구현 캔 Skip
과 Take
:
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
}
}
이 쓰레기의 없음 Skip
과 Take
여러 페이지에 관심이 있다면 이는 매우 비효율적 일 수 없습니다.
( 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를위한 것이 아니며 이것이 좋은 쿼리로 바뀔 수 있는지조차 모릅니다.
Lukazoid의 답변과 유사하게 IQueryable에 대한 확장을 만들었습니다.
public static IEnumerable<IEnumerable<T>> PageIterator<T>(this IQueryable<T> source, int pageSize)
{
Contract.Requires(source != null);
Contract.Requires(pageSize > 0);
Contract.Ensures(Contract.Result<IEnumerable<IQueryable<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);
}
}
}
Skip 또는 Take가 지원되지 않는 경우 유용합니다.
이 확장 방법을 사용합니다.
public static IQueryable<T> Page<T, TResult>(this IQueryable<T> obj, int page, int pageSize, System.Linq.Expressions.Expression<Func<T, TResult>> keySelector, bool asc, out int rowsCount)
{
rowsCount = obj.Count();
int innerRows = rowsCount - (page * pageSize);
if (innerRows < 0)
{
innerRows = 0;
}
if (asc)
return obj.OrderByDescending(keySelector).Take(innerRows).OrderBy(keySelector).Take(pageSize).AsQueryable();
else
return obj.OrderBy(keySelector).Take(innerRows).OrderByDescending(keySelector).Take(pageSize).AsQueryable();
}
public IEnumerable<Data> GetAll(int RowIndex, int PageSize, string SortExpression)
{
int totalRows;
int pageIndex = RowIndex / PageSize;
List<Data> data= new List<Data>();
IEnumerable<Data> dataPage;
bool asc = !SortExpression.Contains("DESC");
switch (SortExpression.Split(' ')[0])
{
case "ColumnName":
dataPage = DataContext.Data.Page(pageIndex, PageSize, p => p.ColumnName, asc, out totalRows);
break;
default:
dataPage = DataContext.vwClientDetails1s.Page(pageIndex, PageSize, p => p.IdColumn, asc, out totalRows);
break;
}
foreach (var d in dataPage)
{
clients.Add(d);
}
return data;
}
public int CountAll()
{
return DataContext.Data.Count();
}
public LightDataTable PagerSelection(int pageNumber, int setsPerPage, Func<LightDataRow, bool> prection = null)
{
this.setsPerPage = setsPerPage;
this.pageNumber = pageNumber > 0 ? pageNumber - 1 : pageNumber;
if (!ValidatePagerByPageNumber(pageNumber))
return this;
var rowList = rows.Cast<LightDataRow>();
if (prection != null)
rowList = rows.Where(prection).ToList();
if (!rowList.Any())
return new LightDataTable() { TablePrimaryKey = this.tablePrimaryKey };
//if (rowList.Count() < (pageNumber * setsPerPage))
// return new LightDataTable(new LightDataRowCollection(rowList)) { TablePrimaryKey = this.tablePrimaryKey };
return new LightDataTable(new LightDataRowCollection(rowList.Skip(this.pageNumber * setsPerPage).Take(setsPerPage).ToList())) { TablePrimaryKey = this.tablePrimaryKey };
}
이것이 내가 한 일입니다. 일반적으로 1에서 시작하지만 IList에서는 0으로 시작합니다. 따라서 8 개의 페이징이 있음을 의미하는 152 개의 행이 있지만 IList에는 7 개만있는 경우 홉 이것은 명확하게 할 수 있습니다.
var results = (medicineInfo.OrderBy(x=>x.id)
.Skip((pages -1) * 2)
.Take(2));
두 가지 주요 옵션이 있습니다.
.NET> = 4.0 동적 LINQ :
- System.Linq.Dynamic을 사용하여 추가하십시오. 상단에.
- 사용하다:
var people = people.AsQueryable().OrderBy("Make ASC, Year DESC").ToList();
NuGet으로 도 얻을 수 있습니다 .
.NET <4.0 확장 방법 :
private static readonly Hashtable accessors = new Hashtable();
private static readonly Hashtable callSites = new Hashtable();
private static CallSite<Func<CallSite, object, object>> GetCallSiteLocked(string name) {
var callSite = (CallSite<Func<CallSite, object, object>>)callSites[name];
if(callSite == null)
{
callSites[name] = callSite = CallSite<Func<CallSite, object, object>>.Create(
Binder.GetMember(CSharpBinderFlags.None, name, typeof(AccessorCache),
new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
}
return callSite;
}
internal static Func<dynamic,object> GetAccessor(string name)
{
Func<dynamic, object> accessor = (Func<dynamic, object>)accessors[name];
if (accessor == null)
{
lock (accessors )
{
accessor = (Func<dynamic, object>)accessors[name];
if (accessor == null)
{
if(name.IndexOf('.') >= 0) {
string[] props = name.Split('.');
CallSite<Func<CallSite, object, object>>[] arr = Array.ConvertAll(props, GetCallSiteLocked);
accessor = target =>
{
object val = (object)target;
for (int i = 0; i < arr.Length; i++)
{
var cs = arr[i];
val = cs.Target(cs, val);
}
return val;
};
} else {
var callSite = GetCallSiteLocked(name);
accessor = target =>
{
return callSite.Target(callSite, (object)target);
};
}
accessors[name] = accessor;
}
}
}
return accessor;
}
public static IOrderedEnumerable<dynamic> OrderBy(this IEnumerable<dynamic> source, string property)
{
return Enumerable.OrderBy<dynamic, object>(source, AccessorCache.GetAccessor(property), Comparer<object>.Default);
}
public static IOrderedEnumerable<dynamic> OrderByDescending(this IEnumerable<dynamic> source, string property)
{
return Enumerable.OrderByDescending<dynamic, object>(source, AccessorCache.GetAccessor(property), Comparer<object>.Default);
}
public static IOrderedEnumerable<dynamic> ThenBy(this IOrderedEnumerable<dynamic> source, string property)
{
return Enumerable.ThenBy<dynamic, object>(source, AccessorCache.GetAccessor(property), Comparer<object>.Default);
}
public static IOrderedEnumerable<dynamic> ThenByDescending(this IOrderedEnumerable<dynamic> source, string property)
{
return Enumerable.ThenByDescending<dynamic, object>(source, AccessorCache.GetAccessor(property), Comparer<object>.Default);
}
참고 URL : https://stackoverflow.com/questions/2380413/paging-with-linq-for-objects
'program story' 카테고리의 다른 글
jQuery에서 $ (this) 선택한 옵션을 얻는 방법은 무엇입니까? (0) | 2020.10.06 |
---|---|
iOS 용 구문 분석 : 앱 실행시 오류 (0) | 2020.10.06 |
Android Studio의 에뮬레이터에 APK를 설치하는 방법은 무엇입니까? (0) | 2020.10.06 |
번 들러가 JSON gem을 설치하지 않는 이유는 무엇입니까? (0) | 2020.10.06 |
객체-관계형 매핑 프레임 워크 란 무엇입니까? (0) | 2020.10.06 |