views:

371

answers:

2

I'm using Troy Goode's paged List in my project.
Normally you just feed it an IEnumerable, a startindex and an item count and it all works.
Now however I'm trying to feed it an IEnumerable I generate as follows:

private static IEnumerable<Color> GetColors(Query query)
{
 IndexSearcher searcher = new IndexSearcher(luceneIndexpath);
 Hits hitColl = searcher.Search(query);
 //Get all the unique colorId's
 List<int> ids = new List<int>();            
 int id = 0;
 for (int i = 0; i < hitColl.Length(); i++)
 {
  if (Int32.TryParse(hitColl.Doc(i).GetField("id").StringValue(), out id))
   ids.Add(id);                
 }
 foreach (int uniqueId in ids.Distinct<int>())
 {
  yield return ColorService.GetColor(uniqueId);
 }
}

--EDIT-- The pagedList works, but requests the yield for ALL my Color objects instead of only the paged ones. This off course defeats the whole use of PagedList and can result in massive enumerations.

--EDIT--
What I think I need is a way to implement Count() so I can make it return the count from ids.Distinct(int) instead of creating all the objects through ColorService.GetColor() and then counting that list.

A: 

It looks like it will accept IQueryable<T>, so just call .AsQueryable() on your IEnumerable<T> and it should work. Personally, though, I'd just use .Skip() and .Take() to get the page - so [your enumerable].Skip(20).Take(10) is page 3 based on a page size of 10. You can easily wrap this into an extension method:

public static IEnumerable<T> GetPage<T>(
    this IEnumerable<T> source,
    int pageIndex, int pageSize)
{
    return source.Skip(pageIndex * pageSize).Take(pageSize);
}

Then that would be [your enumerable].GetPage(3, 10).

Marc Gravell
It already takes IEnumerable and calls AsQueryable if necessary.
Jon Skeet
d'oh! Scratch that, then...
Marc Gravell
+3  A: 

1) PagedList is going to iterate through your data at least twice by the looks of it - once to count it, and then once to fetch the right page. Make sure this doesn't screw things up - or consider caching in a list or some other "cheap" buffer to avoid having to query twice.

2) If you call ToList() on your yielded result before passing it to the paged list, does it look correct?

3) If you just use your GetColors() method and dump out everything it yields, does that look correct?

Basically you need to try to work out whether the problem is with GetColors, PagedList, or the interaction between the two.

EDIT: The only way of "short-cutting" Count() is to implement IList or IList<T>. However, at that point you'd either have to do it properly or just override Count and implement IEnumerable again. I think calling ToList() and using the result is likely to be a lot quicker unless you really have a massive list that you don't want to keep in memory.

Jon Skeet
The method itself works fine. The ToList() method returns ALL my colors. The PagedList's Skip(x).Take(x) is where it seems to go wrong.
borisCallens
And the count. See OP
borisCallens
So if you call ToList() and pass *that* to PagedList, does that work fine?
Jon Skeet
Thing is it always worked, but it didn't page. I made some corrections to the OP to clarify (topmost edit).
borisCallens
Calling ToList will first create ALL my objects (the ColorService call) and then page it if I'm not mistaking. This kind of defeats the purpose of the PagedList.
borisCallens
I wasn't sure what the exact point of PagedList was to start with. But yes, it will create everything. Bear in mind that by the time you yield anything, you've already fetched all the IDs... you just haven't created the colors.
Jon Skeet
Yes, fetching the id's is near instant. Creating the colors takes longer becaus it involves hitting the DB, then depending on some businessrules set some properties and fetch some related colors.The total colors can run up to several K, depending on the query.
borisCallens