tags:

views:

89

answers:

8

For every single LINQ query I've written, I've always used a foreach loop to go through the result. Now, I have a program where I want to get the first 50 rows of the result, do some calculations with those rows, then get the next 50 rows of the result etc.

What is the good standards way to do this using LINQ and C#?

+3  A: 

.Skip().Take() is going to be the best method (as far as clarity goes) in this case. For example:

var results = db.Table.Select(); // Your query here

int rowCount = results.Count(); // Count rows once and store

for(int i = 0; i <= rowCount; i += 50)
{
    var paged = results.Skip(i).Take(50);

    // do the calculations with the groups of 50 here
}

It is worth noting that even though this seems more clear (you're taking groups of 50 from the results), each time you call Skip() and Take() there's a chance that the collection has to be enumerated over again...which is actually less efficient than simply using foreach to enumerate over the results a single time.

Justin Niessner
Mere seconds!!!
Will
This will iterate over the set several times if the object is `IEnumerable<>`
Matthew Whited
Specific scenarios could also show more efficiency moving selection into the loop construct as well. I'm also not sure in what cases .Take will fail from an empty enumeration (iow, rowCount being unnecessary).
Marc
If this is against `IQueryable` objects it is very possible that the paging with be moved to the database. In those cases I highly suggest this approach.
Matthew Whited
+2  A: 

Without knowing anything about the nature of the data and/or the calculations, the "good standards" way to do this would probably be a foreach loop!

It would help if you could provide some information about the nature of the data and the calculations that you want to perform. Depending on that, the best answer might be foreach, Skip/Take, GroupBy or maybe something else altogether.

LukeH
+1  A: 

Use Skip() and Take().

Will
A: 

Use Take, TakeWhile, Skip, SkipWhile

Eugene Cheverda
A: 

Probably you need to use Take() and Skip() to paginate result and apply your logic to each subset:

http://stackoverflow.com/questions/793718/paginated-search-results-with-linq-to-sql

http://blog.bluecog.co.nz/archives/2007/03/23/simple-paging-with-linq-for-sql/

mamoo
A: 

Something to this effect

var page = (From c In db.Customers Order By c.ContactName, c.City Descending Select c).Skip(50).Take(50)

Prakash
+4  A: 

You could use a group by ie:

data.Select((item, index) => new {item, index}).GroupBy(g => g.index / 50)

then you'd do your operation on each group.

similar to this: http://stackoverflow.com/questions/860305/how-to-use-linq-to-group-every-n-number-of-rows

John Boker
A: 

Using .Skip(...).Take(...) will work for paging. But something to consider is that again an IEnumerable<...> this will recalculate the enumerable each time it is processed. Is this is something that is consumed instead of being resettable you could have issues. This method set can fix this problem.

public static class EnumerableEx
{
    public static IEnumerable<IEnumerable<T>> AsPages<T>(
        this IEnumerable<T> set, int pageLength)
    {
        using (var e = set.GetEnumerator())
            while (true)
                yield return GetPage(e, pageLength);
    }
    private static IEnumerable<T> GetPage<T>(
       IEnumerator<T> set, int pageLength)
    {
        for (var position = 0; 
            position < pageLength && set.MoveNext(); 
            position++)
            yield return set.Current;
    }
}

... Here is a usage example of why it is important.

class Program
{
    private static int _last = 0;
    private static IEnumerable<int> GetValues()
    {
        while (true)
            yield return _last++;
    }

    static void Main(string[] args)
    {
        for (var i = 0; i < 3; i++)
            foreach (var value in GetValues().Skip(5 * i).Take(5))
                Console.Write(value + ",");
        // 0,1,2,3,4,10,11,12,13,14,25,26,27,28,29,
        Console.WriteLine();
        _last = 0;

        foreach (var page in GetValues().AsPages(5).Take(3))
            foreach (var value in page)
                Console.Write(value + ",");
        // 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,
    }
}
Matthew Whited