Comment implémenteriez-vous la pagination dans une requête LINQ? Pour l’instant, je serais satisfait si la fonction sql TOP pouvait être imitée. Cependant, je suis sûr que le besoin d’un soutien complet en matière de radiomessagerie apparaît de toute façon plus tôt.
var queryResult = from o in objects
where ...
select new
{
A = o.a,
B = o.b
}
????????? TOP 10????????
Vous recherchez les méthodes d'extension Skip
et Take
. Skip
dépasse les N premiers éléments du résultat et renvoie le reste; Take
renvoie les N premiers éléments du résultat, en supprimant les éléments restants.
Voir MSDN pour plus d'informations sur l'utilisation de ces méthodes: http://msdn.Microsoft.com/en-us/library/bb386988.aspx
Par exemple:
int numberOfObjectsPerPage = 10;
var queryResultPage = queryResult
.Skip(numberOfObjectsPerPage * pageNumber)
.Take(numberOfObjectsPerPage);
Utiliser Skip
et Take
est définitivement la voie à suivre. Si je l'implémentais, j'écrirais probablement ma propre méthode d'extension pour gérer la pagination (pour rendre le code plus lisible). L’implémentation peut bien sûr utiliser Skip
et 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);
}
}
La classe définit deux méthodes d'extension - une pour IEnumerable
et une pour IQueryable
, ce qui signifie que vous pouvez l'utiliser avec LINQ to Objects et LINQ to SQL (lors de l'écriture d'une requête dans la base de données, le compilateur choisira la version IQueryable
.
En fonction de vos besoins en matière de pagination, vous pouvez également ajouter un comportement supplémentaire (par exemple, pour gérer des valeurs pageSize
ou page
négatives). Voici un exemple d'utilisation de cette méthode d'extension dans votre requête:
var q = (from p in products
where p.Show == true
select new { p.Name }).Page(10, pageIndex);
Voici mon approche performante de la pagination lorsque j'utilise LINQ avec des objets:
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);
}
}
}
Ceci peut alors être utilisé comme suit:
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
}
}
Aucune de ces ordures Skip
et Take
qui sera très inefficace si vous êtes intéressé par plusieurs pages.
( for o in objects
where ...
select new
{
A=o.a,
B=o.b
})
.Skip((page-1)*pageSize)
.Take(pageSize)
Je ne sais pas si cela aidera quelqu'un, mais je l'ai trouvé utile pour mes objectifs:
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++;
}
}
Pour utiliser ceci, vous auriez une requête linq et passeriez le résultat avec la taille de la page dans une boucle 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
}
Donc, cela va parcourir chaque auteur en recherchant 100 auteurs à la fois.
EDIT - Skip supprimé (0) car ce n'est pas nécessaire
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 sera évidemment un entier. Cela tire parti du fait que les entiers suppriment simplement les décimales.
Je plaisante à moitié avec cette réponse, mais elle fera ce que vous voulez, et comme elle est différée, vous ne risquez pas une pénalité de performances importante
pages.First(p => p.Key == thePage)
Cette solution ne concerne pas LinqToEntities, je ne sais même pas si cela pourrait en faire une bonne requête.
Similaire à La réponse de Lukazoid J'ai créé une extension pour 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);
}
}
}
C'est utile si Skip ou Take ne sont pas supportés.
Il y a deux options principales:
.NET> = 4.0LINQ dynamique :
var people = people.AsQueryable().OrderBy("Make ASC, Year DESC").ToList();
Vous pouvez également l'obtenir par NuGet .
.NET <4.0Méthodes d'extension :
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);
}
J'utilise cette méthode d'extension:
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 };
}
c’est ce que j’ai fait . Normalement, vous commencez à 1 mais dans IList vous commencez avec 0 . Donc si vous avez 152 lignes, cela signifie que vous en avez 8, mais dans IList vous n’avez que 7 . peut clarifier les choses pour vous
var results = (medicineInfo.OrderBy(x=>x.id)
.Skip((pages -1) * 2)
.Take(2));