Click here to Skip to main content
15,910,878 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Hi,

I have a stored procedure that return results from a few joined tables. I want to page the results at the server.

I get a ISingleResult<type> result. I have a number of extension methods for paging and here is what I have so far:

C#
public static List<T> GetList<T>(this ISingleResult<T> source, int pageSize, int pageIndex, out int resultCount)
{

    List<T> result = null;
    int count = 0;
    TransactionWrapper(() =>
    {
        IEnumerable<T> agg = source;
        count = source.Count();
        if (pageSize > 0)
        {
            agg = agg.Skip(pageSize * pageIndex);
            agg = agg.Take(pageSize);
        }
        result = agg.ToList();
    });
    resultCount = count;
    return result;
}


I get the "the query results cannot be enumerated more than once".

Is there a way around this?

If not then I will have to change my SP into a view and lose a little query functionality, which is livable.

Thanks ^_^
Andy
Posted
Comments
Kornfeld Eliyahu Peter 27-Jul-15 6:39am    
Does it happening also if pageSize is 0 and also when not zero?
Andy Lanng 27-Jul-15 6:41am    
PageSize will always be > zero. I can try with zero but I'm not sure what that would achieve ?
Kornfeld Eliyahu Peter 27-Jul-15 6:42am    
Before that try this:
agg.Skip(...).Take(...).ToList();
Kornfeld Eliyahu Peter 27-Jul-15 6:44am    
As for the zero part...Just to explain my thinking...
When I see an if I always ask myself what happening in each branch -so that the reason I asking you about that zero...
And! If pageSize ALWAYS greater than zero, why the if?!
Andy Lanng 27-Jul-15 6:46am    
If page size = zero then there is no paging and the whole result should be returned. I see what you mean, now. The error does not occur when PageSize = zero.
(my own code and I had to take a second look :Þ )

1 solution

Stored procedures cannot be composed; there's no way to select a certain range of rows returned from the procedure.

There appear to be four options:
  1. Use .ToList(), as Er Daljeet Singh suggested in the comments:
    C#
    result = source.ToList();
    count = result.Count;
    
    if (pageSize > 0)
    {
        result = result.Skip(pageSize * pageIndex).Take(pageSize).ToList();
    }

  2. Use two separate stored procedures - one to count the total results, and one to retrieve a single page of data.
  3. Convert your stored procedure to a view or a table valued function[^].
  4. Use a custom extension method to extract a single page of data and count the total number of records in one hit. That way, you don't have to copy all of the results to a List<T> and then throw most of them away.

    Something like this should work:
    C#
    public static class Extensions
    {
        public static List<T> GetPage<T>(this IEnumerable<T> source, int pageSize, int pageIndex, out int totalCount)
        {
            if (source == null) throw new ArgumentNullException("source");
            if (pageSize < 0) throw new ArgumentOutOfRangeException("pageSize");
            if (pageIndex < 0) throw new ArgumentOutOfRangeException("pageIndex");
            
            if (pageSize == 0)
            {
                var result = source.ToList();
                totalCount = result.Count;
                return result;
            }
            
            int index = 0;
            int startIndex = pageSize * pageIndex;
            int endIndex = startIndex + pageSize;
            var pageOfData = new List<T>(pageSize);
            
            foreach (T item in source)
            {
                if (startIndex <= index && index < endIndex)
                {
                    pageOfData.Add(item);
                }
                
                index++;
            }
            
            totalCount = index;
            return pageOfData;
        }
    }
    
    ...
    
    public static List<T> GetList<T>(this ISingleResult<T> source, int pageSize, int pageIndex, out int resultCount)
    {
        List<T> result = null;
        int count = 0;
    	
        TransactionWrapper(() =>
        {
    	    result = source.GetPage(pageSize, pageIndex, out count);
        });
    	
        resultCount = count;
        return result;
    }

 
Share this answer
 
Comments
Andy Lanng 27-Jul-15 10:08am    
Thanks. This confirms what I suspected. I now have a view instead. I just need to add the parameter of the SP to the query. I guess that means I went with door number 3.

PS: I copied from the GetList(this IEnumerable<t>) extension I already have. It looks much like your GetPage, but is overloaded such that "GetPage" probably wasn't the best name for it anymore.

I really wanted to know if I could if I could use a Stored Procedure in a multi-part query. Your answer is full and complete ^_^

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900