LINQ ऑब्जेक्ट्स के साथ पेजिंग


90

आप LINQ क्वेरी में पेजिंग कैसे लागू करेंगे? वास्तव में कुछ समय के लिए, मुझे संतोष होगा कि यदि sql TOP फ़ंक्शन का अनुकरण किया जा सकता है। हालाँकि, मुझे यकीन है कि पूर्ण पेजिंग समर्थन की आवश्यकता जल्द ही वैसे भी बाद में आती है।

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

जवाबों:


231

आप विस्तार Skipऔर Takeविस्तार के तरीकों की तलाश कर रहे हैं । Skipपरिणाम में पहले N तत्वों को पीछे छोड़ता है, शेष को वापस करता है; Takeपरिणाम में पहले एन तत्वों को वापस करता है, किसी भी शेष तत्वों को छोड़ देता है।

इन विधियों का उपयोग करने के तरीके के बारे में अधिक जानकारी के लिए MSDN देखें: http://msdn.microsoft.com/en-us/library/bb386988.aspx

यह मानते हुए कि आप पहले से ही इस बात को ध्यान में रख रहे हैं कि पेजनंबर 0 से शुरू होना चाहिए (टिप्पणियों में दिए गए अनुसार 1 प्रति घटाएं) आप इसे इस तरह से कर सकते हैं:

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

अन्यथा @ आल्विन द्वारा सुझाव दिया गया

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

7
क्या मुझे एक विशाल डेटाबेस के साथ SQL पर एक ही तकनीक का उपयोग करना चाहिए, क्या यह पूरी तालिका को पहले मेमोरी में ले जाएगा और फिर अवांछित को फेंक देगा?
15:25 बजे user256890

1
यदि आप रुचि रखते हैं कि हुड के नीचे क्या हो रहा है, वैसे, अधिकांश LINQ डेटाबेस ड्राइवर वास्तविक SQL के लिए डिबग आउटपुट जानकारी प्राप्त करने का एक तरीका प्रदान करते हैं जिसे निष्पादित किया जा रहा है।
डेविड फ़फ़र

रॉब कॉनरी ने एक PagedList <T> वर्ग के बारे में ब्लॉग किया जो आपको आरंभ करने में मदद कर सकता है। blog.wekeroad.com/blog/aspnet-mvc-pagedlistt
jrotello

49
इसका परिणाम यह होगा कि पहला पेज IF पेजनंबर शून्य (0) आधारित नहीं है। अगर पेजनंबर 1 से शुरू होता है, तो इस का उपयोग करें ".Skip (numberOfObjectsPerPage * (पेजनंबर - 1))"
एल्विन

परिणामी एसक्यूएल की तरह क्या होगा, एक डेटाबेस मार?
फैज

53

उपयोग करना 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, जिसका अर्थ है कि आप इसे LINQ से ऑब्जेक्ट्स और LINQ से SQL तक (डेटाबेस क्वेरी लिखते समय, कंपाइलर IQueryableसंस्करण को चुनेंगे) दोनों के साथ उपयोग कर सकते हैं ।

आपकी पेजिंग आवश्यकताओं के आधार पर, आप कुछ अतिरिक्त व्यवहार भी जोड़ सकते हैं (उदाहरण के लिए नकारात्मक pageSizeया pageमान को संभालने के लिए )। यहाँ एक उदाहरण है कि आप अपनी क्वेरी में इस एक्सटेंशन विधि का उपयोग कैसे करेंगे:

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

3
मेरा मानना ​​है कि यह पूरा परिणाम सेट लौटा देगा, और फिर सर्वर पर इन-मेमोरी को फ़िल्टर करेगा। यदि यह SQL है तो एक डेटाबेस के खिलाफ बहुत बड़ा प्रदर्शन।
jvenema

1
@jvenema तुम सही हो। चूंकि यह IEnumerableइंटरफ़ेस का उपयोग कर रहा है, बजाय इसके कि IQueryableयह पूरे डेटाबेस तालिका में खींच जाएगा, जो एक प्रमुख प्रदर्शन हिट होगा।
डेविड फेफर

2
आप निश्चित रूप से IQueryableडेटाबेस प्रश्नों के साथ भी काम करने के लिए एक अधिभार जोड़ सकते हैं (मैंने उत्तर को संपादित किया और इसे जोड़ा)। यह थोड़ा दुर्भाग्यपूर्ण है कि आप कोड को पूरी तरह से सामान्य तरीके से नहीं लिख सकते हैं (हास्केल में यह संभव है कि प्रकार की कक्षाओं के साथ)। मूल प्रश्न में LINQ का उल्लेख किया गया है, इसलिए मैंने केवल एक अधिभार लिखा है।
टॉमस पेट्रीक

मैं खुद इसे लागू करने के बारे में सोच रहा था। मैं थोड़ा हैरान हूं कि यह मानक कार्यान्वयन का हिस्सा नहीं है। नमूना कोड के लिए धन्यवाद!
माइकल रिचर्डसन

1
मुझे लगता है कि उदाहरण होना चाहिए: सार्वजनिक स्थैतिक IQueryable <T> पृष्ठ <T> (... आदि
डेविड टैलबोट

37

LINQ ऑब्जेक्ट्स का उपयोग करते समय पेजिंग के लिए मेरा परफॉर्मेंट अप्रोच है:

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यदि आप कई पृष्ठों में रुचि रखते हैं तो यह अत्यधिक अक्षम होगा।



4
यह बस चुराया जाना था और मेरे सामान्य दायित्व में डाल दिया, धन्यवाद! मैंने केवल बनाम अस्पष्टता Paginateको हटाने के लिए विधि का नाम बदला । nounverb
गेब्रियलियस


6

पता नहीं यह किसी की मदद करेगा, लेकिन मैंने इसे अपने उद्देश्यों के लिए उपयोगी पाया:

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 क्वेरी होगी, और पृष्ठ आकार के साथ परिणाम को फ़ॉरच लूप में पास करें:

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 लेखकों को लाने वाले प्रत्येक लेखक पर प्रसारित होगा।


जैसा कि गणना () संग्रह में शामिल है, आप बस इसे सूची () में परिवर्तित कर सकते हैं और अनुक्रमित के साथ पुनरावृत्त कर सकते हैं।
काबर

5

EDIT - स्किप (0) को हटा दें क्योंकि यह आवश्यक नहीं है

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

2
क्या आपको टेक / स्किप के तरीकों का क्रम नहीं बदलना चाहिए? टेक (0) के बाद छोड़ो का कोई मतलब नहीं है। क्वेरी शैली में अपना उदाहरण देने के लिए थैंक्स।
1525 बजे user256890

2
नहीं, वह सही है। Take10, Skip0 पहले 10 तत्वों को लेता है। Skip0 व्यर्थ है और कभी भी नहीं किया जाना चाहिए। और का क्रम Takeऔर Skipमायने रखता है - Skip10, Take10 तत्वों को 10-20 लेता है; Take10, Skip10 रिटर्न कोई तत्व नहीं।
डेविड फेफर

टेक कॉल करने से पहले आपको क्वेरी के आसपास कोष्ठक की भी आवश्यकता हो सकती है। (से ... सेलेक्ट ...)। (10) ले लो। मैंने एक स्ट्रिंग का चयन करने के साथ निर्माण को बुलाया। कोष्ठक के बिना, क्वेरी परिणाम को सीमित करने के बजाय टेक ने स्ट्रिंग के पहले 10
चार्ट लौटाए

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

बैचसाइज़ जाहिर तौर पर एक पूर्णांक होगा। यह इस तथ्य का लाभ उठाता है कि पूर्णांक केवल दशमलव स्थानों को छोड़ देते हैं।

मैं इस प्रतिक्रिया के साथ आधा मजाक कर रहा हूं, लेकिन यह वही करेगा जो आप इसे चाहते हैं, और क्योंकि यह आस्थगित है, यदि आप ऐसा करते हैं तो आप बड़े प्रदर्शन का जुर्माना नहीं लेंगे

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

यह समाधान LinqToEntities के लिए नहीं है, मुझे नहीं पता कि क्या यह इसे एक अच्छी क्वेरी में बदल सकता है।


3

लुकाज़ोइड के उत्तर के समान मैंने 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);
                    }
                }
            }

यह उपयोगी है अगर स्किप या टेक समर्थित नहीं हैं।


1

मैं इस विस्तार विधि का उपयोग करता हूं:

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

1
    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. से शुरू करते हैं, इसलिए यदि आपके पास 152 पंक्तियाँ हैं, जिसका मतलब है कि आपके पास 8 पेजिंग हैं, लेकिन IList में आपके पास केवल 7. आशा है कि यह आपके लिए बात को स्पष्ट कर सकती है



1

दो मुख्य विकल्प हैं:

.NET> = 4.0 डायनामिक LINQ :

  1. System.Linq.Dynamic का उपयोग करके जोड़ें; शीर्ष पर।
  2. उपयोग: 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);
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.