This is a migrated thread and some comments may be shown as answers.

Virtual Scrolling and toDataSourceRequestString()

5 Answers 513 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
Yuriy
Top achievements
Rank 1
Yuriy asked on 23 Nov 2017, 11:31 AM

Hi all,

I've implemented Virtual Scrolling with Custom Binding Directive.
Unfortunately, I have an issue when converting State to query string using toDataSourceRequestString: 

if virtual scrolling requests to skip 49 records, with pageSize =100 it gets converted by toDataSourceRequestString to Page=1&pageSize=100 
and incorrect results are returned. 

Please suggest fix or workaround.

Thanks,
Yuriy  

5 Answers, 1 is accepted

Sort by
0
Svet
Telerik team
answered on 24 Nov 2017, 04:03 PM
Hi Yuriy,

Indeed, this is the expected behavior. If skip is 49 and pageSize is 100, passing the state to the toDataSourceRequestString method will return "page=1&pageSize=100".

This is valid because when we skip 49 rows we are still on page one and virtual scrolling requires to always load a batch equal to the [pageSize] property.

Please check the following sample plunker and inspect the browser console for clarification (scroll until you pass row 100 in order to trigger the dataStateChange event):

http://plnkr.co/edit/VopERvycY4vQHwIodDxd?p=preview
 
I hope this helps. Please let me know in case you need further assistance for this case.

Regards,
Svetlin
Progress Telerik
Try our brand new, jQuery-free Angular components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
Frank
Top achievements
Rank 1
commented on 31 May 2021, 11:36 AM

Hi Svetlin, i am running into a similar issue, i was expecting the api call to return records 50-149. as it is i get very awkward results and the last page never loads as a result
0
Yuriy
Top achievements
Rank 1
answered on 25 Nov 2017, 12:16 PM

Hi Svetlin,

thank you for the answer. 
I did examined your example and found this part:
 private loadProducts(): void {
        this.gridView = {
            data: this.data.slice(this.skip, this.skip + this.pageSize),
            total: this.data.length
        };
    }

This is correct, you fetched a right portion of data, because you have a SKIP and TAKE. 

In my case if, for example, when bind method is called of my custom DataBindingDirective, state={skip:49, take=100}. So, I pass this to toDataSourceRequestString(state) and then request goes to my WebAPI service. It receives only "?page=1&pageSize=100" and thus not able to skip first part of data, since SKIP=49 is lost. 

Thanks,

Yuriy

0
Svet
Telerik team
answered on 27 Nov 2017, 03:22 PM
Hi Yuriy,

The Kendo UI for Angular implementation is slightly different than the one for Kendo UI for JQuery. 

When integrating Kendo UI for Angular with ASP.NET Core we can pass the skip number to the server only as a separate parameter. (This is due to the fact that DataSourceRequest type does not have a Skip property)

This infers that the developer needs to implement custom server side methods for grouping, sorting, filtering, and paging. An example of how these can be implemented can be seen at the following link in the CustomAjaxBindingController.cs tab:

https://demos.telerik.com/aspnet-mvc/grid/customajaxbinding

I hope this helps. Please let me know in case you need further assistance for this case.

Regards,
Svetlin
Progress Telerik
Try our brand new, jQuery-free Angular components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
0
Yuriy
Top achievements
Rank 1
answered on 28 Nov 2017, 07:47 AM

Hi <user>,

you wrote "we can pass the skip number to the server only as a separate parameter. ". 
Great, but can I mix my custom SKIP parameter and ToDataSourceResult? It would be nice not to rewrite all IQueryable extensions for filters, sorting, etc.

Thanks,

Yuriy

0
Svet
Telerik team
answered on 29 Nov 2017, 12:28 PM
Hi Yuriy,

Unfortunately, the toDataSourceResult method accepts only an argument of type DataSourceRequest:

public class DataSourceRequest
{
    public DataSourceRequest();
 
    public int Page { get; set; }
    public int PageSize { get; set; }
    public IList<SortDescriptor> Sorts { get; set; }
    public IList<IFilterDescriptor> Filters { get; set; }
    public IList<GroupDescriptor> Groups { get; set; }
    public IList<AggregateDescriptor> Aggregates { get; set; }
}

So, in the built in functionality, a custom skip parameter cannot be mixed with the ToDataSourceResult method.

For further reference (if you want to use the skip as a part of the ToDataSourceResult method a customization of the source code will be required) I am sending the source code for the ToDataSourceResult() method as an IQueryable extension:

private static DataSourceResult CreateDataSourceResult<TModel, TResult>(this IQueryable queryable, DataSourceRequest request, ModelStateDictionary modelState, Func<TModel, TResult> selector)
        {
            var result = new DataSourceResult();
  
            var data = queryable;
  
            var filters = new List<IFilterDescriptor>();
  
            if (request.Filters != null)
            {
                filters.AddRange(request.Filters);
            }
  
            if (filters.Any())
            {
                data = data.Where(filters);
            }
  
            var sort = new List<SortDescriptor>();
  
            if (request.Sorts != null)
            {
                sort.AddRange(request.Sorts);
            }
  
            var temporarySortDescriptors = new List<SortDescriptor>();
  
            IList<GroupDescriptor> groups = new List<GroupDescriptor>();
  
            if (request.Groups != null)
            {
                groups.AddRange(request.Groups);
            }
  
            var aggregates = new List<AggregateDescriptor>();
  
            if (request.Aggregates != null)
            {
                aggregates.AddRange(request.Aggregates);
            }
  
            if (aggregates.Any())
            {
                var dataSource = data.AsQueryable();
  
                var source = dataSource;
                if (filters.Any())
                {
                    source = dataSource.Where(filters);
                }
  
                result.AggregateResults = source.Aggregate(aggregates.SelectMany(a => a.Aggregates));
  
                if (groups.Any() && aggregates.Any())
                {
                    groups.Each(g => g.AggregateFunctions.AddRange(aggregates.SelectMany(a => a.Aggregates)));
                }
            }
  
            result.Total = data.Count();
  
            if (!sort.Any() && queryable.Provider.IsEntityFrameworkProvider())
            {
                // The Entity Framework provider demands OrderBy before calling Skip.
                SortDescriptor sortDescriptor = new SortDescriptor
                {
                    Member = queryable.ElementType.FirstSortableProperty()
                };
                sort.Add(sortDescriptor);
                temporarySortDescriptors.Add(sortDescriptor);
            }
  
            if (groups.Any())
            {
                groups.Reverse().Each(groupDescriptor =>
                {
                    var sortDescriptor = new SortDescriptor
                    {
                        Member = groupDescriptor.Member,
                        SortDirection = groupDescriptor.SortDirection
                    };
  
                    sort.Insert(0, sortDescriptor);
                    temporarySortDescriptors.Add(sortDescriptor);
                });
            }
  
            if (sort.Any())
            {
                data = data.Sort(sort);
            }
  
            var notPagedData = data;
  
            data = data.Page(request.Page - 1, request.PageSize);
  
            if (groups.Any())
            {
                data = data.GroupBy(notPagedData, groups);
            }
  
            result.Data = data.Execute(selector);
  
            if (modelState != null && !modelState.IsValid)
            {
                result.Errors = modelState.SerializeErrors();
            }
  
            temporarySortDescriptors.Each(sortDescriptor => sort.Remove(sortDescriptor));
  
            return result;
        }

All other methods are eventually calling the one above with the respective arguments. Here is the whole code of the QueryableExtensions.cs file:

namespace Kendo.Mvc.Extensions
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web.Mvc;
    using Kendo.Mvc;
    using Kendo.Mvc.Infrastructure;
    using Kendo.Mvc.Infrastructure.Implementation;
    using Infrastructure.Implementation.Expressions;
    using Kendo.Mvc.UI;
  
    /// <summary>
    /// Provides extension methods to process DataSourceRequest.
    /// </summary>
    public static class QueryableExtensions
    {
        private static DataSourceResult ToDataSourceResult(this DataTableWrapper enumerable, DataSourceRequest request)
        {
            var filters = new List<IFilterDescriptor>();
  
            if (request.Filters != null)
            {
                filters.AddRange(request.Filters);
            }
  
            if (filters.Any())
            {
                var dataTable = enumerable.Table;
                filters.SelectMemberDescriptors()
                    .Each(f => f.MemberType = GetFieldByTypeFromDataColumn(dataTable, f.Member));
            }
  
            var group = new List<GroupDescriptor>();
  
            if (request.Groups != null)
            {
                group.AddRange(request.Groups);
            }
  
            if (group.Any())
            {
                var dataTable = enumerable.Table;
                group.Each(g => g.MemberType = GetFieldByTypeFromDataColumn(dataTable, g.Member));
            }
  
            var result = enumerable.AsEnumerable().ToDataSourceResult(request);
            result.Data = result.Data.SerializeToDictionary(enumerable.Table);
  
            return result;
        }
  
        private static Type GetFieldByTypeFromDataColumn(DataTable dataTable, string memberName)
        {
            return dataTable.Columns.Contains(memberName) ? dataTable.Columns[memberName].DataType : null;
        }
  
        /// <summary>
        /// Applies paging, sorting, filtering and grouping using the information from the DataSourceRequest object.
        /// If the collection is already paged, the method returns an empty resullt.
        /// </summary>
        /// <param name="dataTable">An instance of <see cref="DataTable" />.</param>
        /// <param name="request">An instance of <see cref="DataSourceRequest" />.</param>
        /// <returns>
        /// A <see cref="DataSourceResult" /> object, which contains the processed data after
        /// paging, sorting, filtering and grouping are applied.
        /// </returns>
        public static DataSourceResult ToDataSourceResult(this DataTable dataTable, DataSourceRequest request)
        {
            return dataTable.WrapAsEnumerable().ToDataSourceResult(request);
        }
  
        /// <summary>
        /// Applies paging, sorting, filtering and grouping using the information from the DataSourceRequest object.
        /// If the collection is already paged, the method returns an empty resullt.
        /// </summary>
        /// <param name="dataTable">An instance of <see cref="DataTable" />.</param>
        /// <param name="request">An instance of <see cref="DataSourceRequest" />.</param>
        /// <returns>
        /// A Task of <see cref="DataSourceResult" /> object, which contains the processed data after
        /// paging, sorting, filtering and grouping are applied.
        /// It can be called with the "await" keyword for asynchronous operation.
        /// </returns>
        public static Task<DataSourceResult> ToDataSourceResultAsync(this DataTable dataTable, DataSourceRequest request)
        {
            return CreateDataSourceResultAsync(() => QueryableExtensions.ToDataSourceResult(dataTable, request));
        }
  
        /// <summary>
        /// Applies paging, sorting, filtering and grouping using the information from the DataSourceRequest object.
        /// If the collection is already paged, the method returns an empty result.
        /// </summary>
        /// <param name="enumerable">An instance of <see cref="IEnumerable" />.</param>
        /// <param name="request">An instance of <see cref="DataSourceRequest" />.</param>
        /// <returns>
        /// A <see cref="DataSourceResult" /> object, which contains the processed data after
        /// paging, sorting, filtering and grouping are applied.
        /// </returns>
        public static DataSourceResult ToDataSourceResult(this IEnumerable enumerable, DataSourceRequest request)
        {
            return enumerable.AsQueryable().ToDataSourceResult(request);
        }
  
        /// <summary>
        /// Applies paging, sorting, filtering and grouping using the information from the DataSourceRequest object.
        /// If the collection is already paged, the method returns an empty result.
        /// </summary>
        /// <param name="enumerable">An instance of <see cref="IEnumerable" />.</param>
        /// <param name="request">An instance of <see cref="DataSourceRequest" />.</param>
        /// <returns>
        /// A Task of <see cref="DataSourceResult" /> object, which contains the processed data
        /// after paging, sorting, filtering and grouping are applied.
        /// It can be called with the "await" keyword for asynchronous operation.
        /// </returns>
        public static Task<DataSourceResult> ToDataSourceResultAsync(this IEnumerable enumerable, DataSourceRequest request)
        {
            return CreateDataSourceResultAsync(() => QueryableExtensions.ToDataSourceResult(enumerable, request));
        }
  
        public static DataSourceResult ToDataSourceResult(this IEnumerable enumerable, DataSourceRequest request, ModelStateDictionary modelState)
        {
            return enumerable.AsQueryable().ToDataSourceResult(request, modelState);
        }
  
        public static Task<DataSourceResult> ToDataSourceResultAsync(
            this IEnumerable enumerable, DataSourceRequest request, ModelStateDictionary modelState)
        {
            return CreateDataSourceResultAsync(() => QueryableExtensions.ToDataSourceResult(enumerable, request, modelState));
        }
  
        /// <summary>
        /// Applies paging, sorting, filtering and grouping using the information from the DataSourceRequest object.
        /// If the collection is already paged, the method returns an empty result.
        /// </summary>
        /// <param name="queryable">An instance of <see cref="IQueryable" />.</param>
        /// <param name="request">An instance of <see cref="DataSourceRequest" />.</param>
        /// <returns>
        /// A <see cref="DataSourceResult" /> object, which contains the processed data after paging, sorting, filtering and grouping are applied.
        /// </returns>
        public static DataSourceResult ToDataSourceResult(this IQueryable queryable, DataSourceRequest request)
        {
            return queryable.ToDataSourceResult(request, null);
        }
  
        /// <summary>
        /// Applies paging, sorting, filtering and grouping using the information from the DataSourceRequest object.
        /// If the collection is already paged, the method returns an empty result.
        /// </summary>
        /// <param name="queryable">An instance of <see cref="IQueryable" />.</param>
        /// <param name="request">An instance of <see cref="DataSourceRequest" />.</param>
        /// <returns>
        /// A Task of <see cref="DataSourceResult" /> object, which contains the processed data
        /// after paging, sorting, filtering and grouping are applied.
        /// It can be called with the "await" keyword for asynchronous operation.
        /// </returns>
        public static Task<DataSourceResult> ToDataSourceResultAsync(this IQueryable queryable, DataSourceRequest request)
        {
            return CreateDataSourceResultAsync(() => QueryableExtensions.ToDataSourceResult(queryable, request));
        }
  
        public static DataSourceResult ToDataSourceResult<TModel, TResult>(this IEnumerable<TModel> enumerable, DataSourceRequest request, Func<TModel, TResult> selector)
        {
            return enumerable.AsQueryable().CreateDataSourceResult(request, null, selector);
        }
  
        public static Task<DataSourceResult> ToDataSourceResultAsync<TModel, TResult>(
            this IEnumerable<TModel> enumerable, DataSourceRequest request, Func<TModel, TResult> selector)
        {
            return CreateDataSourceResultAsync(() => QueryableExtensions.ToDataSourceResult(enumerable, request, selector));
        }
  
        public static DataSourceResult ToDataSourceResult<TModel, TResult>(this IEnumerable<TModel> enumerable, DataSourceRequest request, ModelStateDictionary modelState, Func<TModel, TResult> selector)
        {
            return enumerable.AsQueryable().CreateDataSourceResult(request, modelState, selector);
        }
  
        public static Task<DataSourceResult> ToDataSourceResultAsync<TModel, TResult>(
            this IEnumerable<TModel> enumerable, DataSourceRequest request, ModelStateDictionary modelState, Func<TModel, TResult> selector)
        {
            return CreateDataSourceResultAsync(() => QueryableExtensions.ToDataSourceResult(enumerable, request, modelState, selector));
        }
  
        public static DataSourceResult ToDataSourceResult<TModel, TResult>(this IQueryable<TModel> enumerable, DataSourceRequest request, Func<TModel, TResult> selector)
        {
            return enumerable.CreateDataSourceResult(request, null, selector);
        }
  
        public static Task<DataSourceResult> ToDataSourceResultAsync<TModel, TResult>
            (this IQueryable<TModel> queryable, DataSourceRequest request, Func<TModel, TResult> selector)
        {
            return CreateDataSourceResultAsync(() => QueryableExtensions.ToDataSourceResult(queryable, request, selector));
        }
  
        public static DataSourceResult ToDataSourceResult<TModel, TResult>(this IQueryable<TModel> enumerable, DataSourceRequest request, ModelStateDictionary modelState, Func<TModel, TResult> selector)
        {
            return enumerable.CreateDataSourceResult(request, modelState, selector);
        }
  
        public static Task<DataSourceResult> ToDataSourceResultAsync<TModel, TResult>(
            this IQueryable<TModel> queryable, DataSourceRequest request, ModelStateDictionary modelState, Func<TModel, TResult> selector)
        {
            return CreateDataSourceResultAsync(() => QueryableExtensions.ToDataSourceResult(queryable, request, modelState, selector)); ;
        }
  
        public static DataSourceResult ToDataSourceResult(this IQueryable queryable, DataSourceRequest request, ModelStateDictionary modelState)
        {
            return queryable.CreateDataSourceResult<object, object>(request, modelState, null);
        }
  
        public static Task<DataSourceResult> ToDataSourceResultAsync(this IQueryable queryable, DataSourceRequest request, ModelStateDictionary modelState)
        {
            return CreateDataSourceResultAsync(() => QueryableExtensions.ToDataSourceResult(queryable, request, modelState));
        }
  
        private static DataSourceResult CreateDataSourceResult<TModel, TResult>(this IQueryable queryable, DataSourceRequest request, ModelStateDictionary modelState, Func<TModel, TResult> selector)
        {
            var result = new DataSourceResult();
  
            var data = queryable;
  
            var filters = new List<IFilterDescriptor>();
  
            if (request.Filters != null)
            {
                filters.AddRange(request.Filters);
            }
  
            if (filters.Any())
            {
                data = data.Where(filters);
            }
  
            var sort = new List<SortDescriptor>();
  
            if (request.Sorts != null)
            {
                sort.AddRange(request.Sorts);
            }
  
            var temporarySortDescriptors = new List<SortDescriptor>();
  
            IList<GroupDescriptor> groups = new List<GroupDescriptor>();
  
            if (request.Groups != null)
            {
                groups.AddRange(request.Groups);
            }
  
            var aggregates = new List<AggregateDescriptor>();
  
            if (request.Aggregates != null)
            {
                aggregates.AddRange(request.Aggregates);
            }
  
            if (aggregates.Any())
            {
                var dataSource = data.AsQueryable();
  
                var source = dataSource;
                if (filters.Any())
                {
                    source = dataSource.Where(filters);
                }
  
                result.AggregateResults = source.Aggregate(aggregates.SelectMany(a => a.Aggregates));
  
                if (groups.Any() && aggregates.Any())
                {
                    groups.Each(g => g.AggregateFunctions.AddRange(aggregates.SelectMany(a => a.Aggregates)));
                }
            }
  
            result.Total = data.Count();
  
            if (!sort.Any() && queryable.Provider.IsEntityFrameworkProvider())
            {
                // The Entity Framework provider demands OrderBy before calling Skip.
                SortDescriptor sortDescriptor = new SortDescriptor
                {
                    Member = queryable.ElementType.FirstSortableProperty()
                };
                sort.Add(sortDescriptor);
                temporarySortDescriptors.Add(sortDescriptor);
            }
  
            if (groups.Any())
            {
                groups.Reverse().Each(groupDescriptor =>
                {
                    var sortDescriptor = new SortDescriptor
                    {
                        Member = groupDescriptor.Member,
                        SortDirection = groupDescriptor.SortDirection
                    };
  
                    sort.Insert(0, sortDescriptor);
                    temporarySortDescriptors.Add(sortDescriptor);
                });
            }
  
            if (sort.Any())
            {
                data = data.Sort(sort);
            }
  
            var notPagedData = data;
  
            data = data.Page(request.Page - 1, request.PageSize);
  
            if (groups.Any())
            {
                data = data.GroupBy(notPagedData, groups);
            }
  
            result.Data = data.Execute(selector);
  
            if (modelState != null && !modelState.IsValid)
            {
                result.Errors = modelState.SerializeErrors();
            }
  
            temporarySortDescriptors.Each(sortDescriptor => sort.Remove(sortDescriptor));
  
            return result;
        }
  
        private static Task<DataSourceResult> CreateDataSourceResultAsync(Func<DataSourceResult> expression)
        {
            // Task.Run() is not available in .NET 4.0
            return Task.Factory.StartNew(expression, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
        }
  
        private static IQueryable CallQueryableMethod(this IQueryable source, string methodName, LambdaExpression selector)
        {
            IQueryable query = source.Provider.CreateQuery(
                Expression.Call(
                    typeof(Queryable),
                    methodName,
                    new[] { source.ElementType, selector.Body.Type },
                    source.Expression,
                    Expression.Quote(selector)));
  
            return query;
        }
  
        /// <summary>
        /// Sorts the elements of a sequence using the specified sort descriptors.
        /// </summary>
        /// <param name="source">A sequence of values to sort.</param>
        /// <param name="sortDescriptors">The sort descriptors used for sorting.</param>
        /// <returns>
        /// An <see cref="IQueryable" /> whose elements are sorted according to a <paramref name="sortDescriptors"/>.
        /// </returns>
        public static IQueryable Sort(this IQueryable source, IEnumerable<SortDescriptor> sortDescriptors)
        {
            var builder = new SortDescriptorCollectionExpressionBuilder(source, sortDescriptors);
            return builder.Sort();
        }
  
        /// <summary>
        /// Pages through the elements of a sequence until the specified
        /// <paramref name="pageIndex"/> using <paramref name="pageSize"/>.
        /// </summary>
        /// <param name="source">A sequence of values to page.</param>
        /// <param name="pageIndex">Index of the page.</param>
        /// <param name="pageSize">Size of the page.</param>
        /// <returns>
        /// An <see cref="IQueryable" /> whose elements are at the specified <paramref name="pageIndex"/>.
        /// </returns>
        public static IQueryable Page(this IQueryable source, int pageIndex, int pageSize)
        {
            IQueryable query = source;
  
            query = query.Skip(pageIndex * pageSize);
  
            if (pageSize > 0)
            {
                query = query.Take(pageSize);
            }
  
            return query;
        }
  
        /// <summary>
        /// Projects each element of a sequence into a new form.
        /// </summary>
        /// <returns>
        /// An <see cref="IQueryable" /> whose elements are the result of invoking a
        /// projection selector on each element of <paramref name="source" />.
        /// </returns>
        /// <param name="source"> A sequence of values to project. </param>
        /// <param name="selector"> A projection function to apply to each element. </param>
        public static IQueryable Select(this IQueryable source, LambdaExpression selector)
        {
            return source.CallQueryableMethod("Select", selector);
        }
  
        /// <summary>
        /// Groups the elements of a sequence according to a specified key selector function.
        /// </summary>
        /// <param name="source"> An <see cref="IQueryable" /> whose elements to group.</param>
        /// <param name="keySelector"> A function to extract the key for each element.</param>
        /// <returns>
        /// An <see cref="IQueryable"/> with <see cref="IGrouping{TKey,TElement}"/> items,
        /// whose elements contains a sequence of objects and a key.
        /// </returns>
        public static IQueryable GroupBy(this IQueryable source, LambdaExpression keySelector)
        {
            return source.CallQueryableMethod("GroupBy", keySelector);
        }
  
        /// <summary>
        /// Sorts the elements of a sequence in ascending order according to a key.
        /// </summary>
        /// <returns>
        /// An <see cref="IQueryable" /> whose elements are sorted according to a key.
        /// </returns>
        /// <param name="source">
        /// A sequence of values to order.
        /// </param>
        /// <param name="keySelector">
        /// A function to extract a key from an element.
        /// </param>
        public static IQueryable OrderBy(this IQueryable source, LambdaExpression keySelector)
        {
            return source.CallQueryableMethod("OrderBy", keySelector);
        }
  
        /// <summary>
        /// Sorts the elements of a sequence in descending order according to a key.
        /// </summary>
        /// <returns>
        /// An <see cref="IQueryable" /> whose elements are sorted in descending order according to a key.
        /// </returns>
        /// <param name="source">
        /// A sequence of values to order.
        /// </param>
        /// <param name="keySelector">
        /// A function to extract a key from an element.
        /// </param>
        public static IQueryable OrderByDescending(this IQueryable source, LambdaExpression keySelector)
        {
            return source.CallQueryableMethod("OrderByDescending", keySelector);
        }
  
        /// <summary>
        /// Calls <see cref="OrderBy(System.Linq.IQueryable,System.Linq.Expressions.LambdaExpression)"/>
        /// or <see cref="OrderByDescending"/> depending on the <paramref name="sortDirection"/>.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="keySelector">The key selector.</param>
        /// <param name="sortDirection">The sort direction.</param>
        /// <returns>
        /// An <see cref="IQueryable" /> whose elements are sorted according to a key.
        /// </returns>
        public static IQueryable OrderBy(this IQueryable source, LambdaExpression keySelector, ListSortDirection? sortDirection)
        {
            if (sortDirection.HasValue)
            {
                if (sortDirection.Value == ListSortDirection.Ascending)
                {
                    return source.OrderBy(keySelector);
                }
  
                return source.OrderByDescending(keySelector);
            }
  
            return source;
        }
  
        /// <summary>
        /// Groups the elements of a sequence according to a specified <paramref name="groupDescriptors"/>.
        /// </summary>
        /// <param name="source"> An <see cref="IQueryable" /> whose elements to group. </param>
        /// <param name="groupDescriptors">The group descriptors used for grouping.</param>
        /// <returns>
        /// An <see cref="IQueryable"/> with <see cref="IGroup"/> items,
        /// whose elements contains a sequence of objects and a key.
        /// </returns>
        public static IQueryable GroupBy(this IQueryable source, IEnumerable<GroupDescriptor> groupDescriptors)
        {
            return source.GroupBy(source, groupDescriptors);
        }
  
        public static IQueryable GroupBy(this IQueryable source, IQueryable notPagedData, IEnumerable<GroupDescriptor> groupDescriptors)
        {
            var builder = new GroupDescriptorCollectionExpressionBuilder(source, groupDescriptors, notPagedData);
            builder.Options.LiftMemberAccessToNull = source.Provider.IsLinqToObjectsProvider();
            return builder.CreateQuery();
        }
  
        /// <summary>
        /// Calculates the results of given aggregates functions on a sequence of elements.
        /// </summary>
        /// <param name="source"> An <see cref="IQueryable" /> whose elements will
        /// be used for aggregate calculation.</param>
        /// <param name="aggregateFunctions">The aggregate functions.</param>
        /// <returns>Collection of <see cref="AggregateResult"/>s calculated for each function.</returns>
        public static AggregateResultCollection Aggregate(this IQueryable source, IEnumerable<AggregateFunction> aggregateFunctions)
        {
            var functions = aggregateFunctions.ToList();
  
            if (functions.Count > 0)
            {
                var builder = new QueryableAggregatesExpressionBuilder(source, functions);
                builder.Options.LiftMemberAccessToNull = source.Provider.IsLinqToObjectsProvider();
                var groups = builder.CreateQuery();
  
                foreach (AggregateFunctionsGroup group in groups)
                {
                    return group.GetAggregateResults(functions);
                }
            }
  
            return new AggregateResultCollection();
        }
  
        /// <summary>
        /// Filters a sequence of values based on a predicate.
        /// </summary>
        /// <returns>
        /// An <see cref="IQueryable" /> that contains elements from the input sequence
        /// that satisfy the condition specified by <paramref name="predicate" />.
        /// </returns>
        /// <param name="source"> An <see cref="IQueryable" /> to filter.</param>
        /// <param name="predicate"> A function to test each element for a condition.</param>
        public static IQueryable Where(this IQueryable source, Expression predicate)
        {
            return source.Provider.CreateQuery(
               Expression.Call(
                   typeof(Queryable),
                   "Where",
                   new[] { source.ElementType },
                   source.Expression,
                   Expression.Quote(predicate)));
        }
  
        /// <summary>
        /// Filters a sequence of values based on a collection of <see cref="IFilterDescriptor"/>.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="filterDescriptors">The filter descriptors.</param>
        /// <returns>
        /// An <see cref="IQueryable" /> that contains elements from the input sequence
        /// that satisfy the conditions specified by each filter descriptor in <paramref name="filterDescriptors" />.
        /// </returns>
        public static IQueryable Where(this IQueryable source, IEnumerable<IFilterDescriptor> filterDescriptors)
        {
            if (filterDescriptors.Any())
            {
                var parameterExpression = Expression.Parameter(source.ElementType, "item");
  
                var expressionBuilder = new FilterDescriptorCollectionExpressionBuilder(parameterExpression, filterDescriptors);
                expressionBuilder.Options.LiftMemberAccessToNull = source.Provider.IsLinqToObjectsProvider();
                var predicate = expressionBuilder.CreateFilterExpression();
                return source.Where(predicate);
            }
  
            return source;
        }
  
        /// <summary>
        /// Returns a specified number of contiguous elements from the start of a sequence.
        /// </summary>
        /// <returns>
        /// An <see cref="IQueryable" /> that contains the specified number
        /// of elements from the start of <paramref name="source" />.
        /// </returns>
        /// <param name="source"> The sequence to return elements from.</param>
        /// <param name="count"> The number of elements to return. </param>
        /// <exception cref="ArgumentNullException"><paramref name="source" /> is null. </exception>
        public static IQueryable Take(this IQueryable source, int count)
        {
            if (source == null) throw new ArgumentNullException("source");
            return source.Provider.CreateQuery(
                Expression.Call(
                    typeof(Queryable), "Take",
                    new Type[] { source.ElementType },
                    source.Expression, Expression.Constant(count)));
        }
  
        /// <summary>
        /// Bypasses a specified number of elements in a sequence
        /// and then returns the remaining elements.
        /// </summary>
        /// <returns>
        /// An <see cref="IQueryable" /> that contains elements that occur
        /// after the specified index in the input sequence.
        /// </returns>
        /// <param name="source">
        /// An <see cref="IQueryable" /> to return elements from.
        /// </param>
        /// <param name="count">
        /// The number of elements to skip before returning the remaining elements.
        /// </param>
        /// <exception cref="ArgumentNullException"> <paramref name="source" /> is null.</exception>
        public static IQueryable Skip(this IQueryable source, int count)
        {
            if (source == null) throw new ArgumentNullException("source");
            return source.Provider.CreateQuery(
                Expression.Call(
                    typeof(Queryable), "Skip",
                    new Type[] { source.ElementType },
                    source.Expression, Expression.Constant(count)));
        }
  
        /// <summary> Returns the number of elements in a sequence.</summary>
        /// <returns> The number of elements in the input sequence.</returns>
        /// <param name="source">
        /// The <see cref="IQueryable" /> that contains the elements to be counted.
        /// </param>
        /// <exception cref="ArgumentNullException"> <paramref name="source" /> is null.</exception>
        public static int Count(this IQueryable source)
        {
            if (source == null) throw new ArgumentNullException("source");
            return source.Provider.Execute<int>(
                Expression.Call(
                    typeof(Queryable), "Count",
                    new Type[] { source.ElementType }, source.Expression));
        }
  
        /// <summary> Returns the element at a specified index in a sequence.</summary>
        /// <returns> The element at the specified position in <paramref name="source" />.</returns>
        /// <param name="source"> An <see cref="IQueryable" /> to return an element from.</param>
        /// <param name="index"> The zero-based index of the element to retrieve.</param>
        /// <exception cref="ArgumentNullException"> <paramref name="source" /> is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException"> <paramref name="index" /> is less than zero.</exception>
        public static object ElementAt(this IQueryable source, int index)
        {
            if (source == null) throw new ArgumentNullException("source");
            if (index < 0) throw new ArgumentOutOfRangeException("index");
  
            return source.Provider.Execute(
                Expression.Call(
                    typeof(Queryable),
                    "ElementAt",
                    new Type[] { source.ElementType },
                    source.Expression,
                    Expression.Constant(index)));
        }
  
        /// <summary>
        /// Produces the set union of two sequences by using the default equality comparer.
        /// </summary>
        /// <returns>
        /// An <see cref="IQueryable" /> that contains the elements from both input sequences, excluding duplicates.
        /// </returns>
        /// <param name="source">
        /// An <see cref="IQueryable" /> whose distinct elements form the first set for the union.
        /// </param>
        /// <param name="second">
        /// An <see cref="IQueryable" /> whose distinct elements form the first set for the union.
        /// </param>
        /// <exception cref="ArgumentNullException"> <paramref name="source" /> is null.</exception>
        public static IQueryable Union(this IQueryable source, IQueryable second)
        {
            IQueryable query = source.Provider.CreateQuery(
                Expression.Call(
                    typeof(Queryable),
                    "Union",
                    new[] { source.ElementType },
                    source.Expression,
                    second.Expression));
  
            return query;
        }
  
        private static IEnumerable Execute<TModel, TResult>(this IQueryable source, Func<TModel, TResult> selector)
        {
            if (source == null) throw new ArgumentNullException("source");
  
            if (source is DataTableWrapper)
            {
                return source;
            }
  
            var type = source.ElementType;
  
  
            if (selector != null)
            {
                var groups = new List<AggregateFunctionsGroup>();
  
                if (type == typeof(AggregateFunctionsGroup))
                {
                    foreach (AggregateFunctionsGroup group in source)
                    {
                        group.Items = group.Items.AsQueryable().Execute(selector);
                        groups.Add(group);
                    }
  
                    return groups;
                }
                else
                {
                    var list = new List<TResult>();
  
                    foreach (TModel item in source)
                    {
                        list.Add(selector(item));
                    }
  
                    return list;
                }
            }
            else
            {
                var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(type));
  
                foreach (var item in source)
                {
                    list.Add(item);
                }
  
                return list;
            }
        }
  
        /// <summary>
        /// Applies sorting, filtering and grouping using the information from the DataSourceRequest object.
        /// If the collection is already paged, the method returns an empty result.
        /// </summary>
        /// <param name="enumerable">An instance of <see cref="IEnumerable" />.</param>
        /// <param name="request">An instance of <see cref="DataSourceRequest" />.</param>
        /// <returns>
        /// A <see cref="TreeDataSourceResult" /> object, which contains the processed data after sorting, filtering and grouping are applied.
        /// </returns>
        public static TreeDataSourceResult ToTreeDataSourceResult(this IEnumerable enumerable, DataSourceRequest request)
        {
            return enumerable.AsQueryable().ToTreeDataSourceResult(request, null);
        }
  
        /// <summary>
        /// Applies sorting, filtering and grouping using the information from the DataSourceRequest object.
        /// If the collection is already paged, the method returns an empty result.
        /// </summary>
        /// <param name="enumerable">An instance of <see cref="IEnumerable" />.</param>
        /// <param name="request">An instance of <see cref="DataSourceRequest" />.</param>
        /// <returns>
        /// A Task of <see cref="TreeDataSourceResult" /> object, which contains the processed data
        /// after sorting, filtering and grouping are applied.
        /// It can be called with the "await" keyword for asynchronous operation.
        /// </returns>
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync(this IEnumerable enumerable, DataSourceRequest request)
        {
            return CreateTreeDataSourceResultAsync(() => QueryableExtensions.ToTreeDataSourceResult(enumerable, request));
        }
  
        public static TreeDataSourceResult ToTreeDataSourceResult(this IEnumerable enumerable, DataSourceRequest request, ModelStateDictionary modelState)
        {
            return enumerable.AsQueryable().CreateTreeDataSourceResult<object, object, object, object>(request, null, null, modelState, null, null);
        }
  
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync(this IEnumerable enumerable,
            DataSourceRequest request, ModelStateDictionary modelState)
        {
            return CreateTreeDataSourceResultAsync(() => QueryableExtensions.ToTreeDataSourceResult(enumerable, request, modelState));
        }
  
        public static TreeDataSourceResult ToTreeDataSourceResult<TModel, TResult>(this IQueryable<TModel> enumerable,
            DataSourceRequest request,
            Func<TModel, TResult> selector)
        {
            return enumerable.ToTreeDataSourceResult<TModel, object, object, TResult>(request, null, null, selector);
        }
  
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync<TModel, TResult>( this IQueryable<TModel> queryable,
            DataSourceRequest request, Func<TModel, TResult> selector)
        {
            return CreateTreeDataSourceResultAsync(() => QueryableExtensions.ToTreeDataSourceResult(queryable, request, selector));
        }
  
        public static TreeDataSourceResult ToTreeDataSourceResult<TModel, TResult>(this IEnumerable<TModel> enumerable,
            DataSourceRequest request,
            Func<TModel, TResult> selector)
        {
            return enumerable.ToTreeDataSourceResult<TModel, object, object, TResult>(request, null, null, selector);
        }
  
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync<TModel, TResult>(this IEnumerable<TModel> enumerable,
            DataSourceRequest request, Func<TModel, TResult> selector)
        {
            return CreateTreeDataSourceResultAsync(() => QueryableExtensions.ToTreeDataSourceResult(enumerable, request, selector));
        }
  
        public static TreeDataSourceResult ToTreeDataSourceResult<TModel, T1, T2>(this IQueryable<TModel> enumerable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector)
        {
            return enumerable.CreateTreeDataSourceResult<TModel, T1, T2, TModel>(request, idSelector, parentIDSelector, null, null, null);
        }
  
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync<TModel, T1, T2>(this IQueryable<TModel> queryable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector)
        {
            return CreateTreeDataSourceResultAsync(() =>
                QueryableExtensions.ToTreeDataSourceResult(queryable, request, idSelector, parentIDSelector));
        }
  
        public static TreeDataSourceResult ToTreeDataSourceResult<TModel, T1, T2>(this IQueryable<TModel> enumerable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Expression<Func<TModel, bool>> rootSelector)
        {
            return enumerable.CreateTreeDataSourceResult<TModel, T1, T2, TModel>(request, idSelector, parentIDSelector, null, null, rootSelector);
        }
  
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync<TModel, T1, T2>(this IQueryable<TModel> queryable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Expression<Func<TModel, bool>> rootSelector)
        {
            return CreateTreeDataSourceResultAsync(() =>
                QueryableExtensions.ToTreeDataSourceResult(queryable, request, idSelector, parentIDSelector, rootSelector));
        }
  
        public static TreeDataSourceResult ToTreeDataSourceResult<TModel, T1, T2, TResult>(this IQueryable<TModel> queryable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Expression<Func<TModel, bool>> rootSelector,
            Func<TModel, TResult> selector)
        {
            return queryable.CreateTreeDataSourceResult(request, idSelector, parentIDSelector, null, selector, rootSelector);
        }
  
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync<TModel, T1, T2, TResult>(this IQueryable<TModel> queryable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Expression<Func<TModel, bool>> rootSelector,
            Func<TModel, TResult> selector)
        {
            return CreateTreeDataSourceResultAsync(() =>
                QueryableExtensions.ToTreeDataSourceResult(queryable, request, idSelector, parentIDSelector, rootSelector, selector));
        }
  
        public static TreeDataSourceResult ToTreeDataSourceResult<TModel, T1, T2>(this IQueryable<TModel> queryable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            ModelStateDictionary modelState)
        {
            return queryable.ToTreeDataSourceResult<TModel, T1, T2, TModel>(request, idSelector, parentIDSelector, modelState, null);
        }
  
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync<TModel, T1, T2>(this IQueryable<TModel> queryable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            ModelStateDictionary modelState)
        {
            return CreateTreeDataSourceResultAsync(() =>
                QueryableExtensions.ToTreeDataSourceResult(queryable, request, idSelector, parentIDSelector, modelState));
        }
  
        public static TreeDataSourceResult ToTreeDataSourceResult<TModel, T1, T2>(this IQueryable<TModel> enumerable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Expression<Func<TModel, bool>> rootSelector,
            ModelStateDictionary modelState)
        {
            return enumerable.CreateTreeDataSourceResult<TModel, T1, T2, TModel>(request, idSelector, parentIDSelector, modelState, null, rootSelector);
        }
  
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync<TModel, T1, T2>(this IQueryable<TModel> queryable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Expression<Func<TModel, bool>> rootSelector,
            ModelStateDictionary modelState)
        {
            return CreateTreeDataSourceResultAsync(() =>
                QueryableExtensions.ToTreeDataSourceResult(queryable, request, idSelector, parentIDSelector, rootSelector, modelState));
        }
  
        public static TreeDataSourceResult ToTreeDataSourceResult<TModel, T1, T2, TResult>(this IQueryable<TModel> queryable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Func<TModel, TResult> selector)
        {
            return queryable.CreateTreeDataSourceResult(request, idSelector, parentIDSelector, null, selector, null);
        }
  
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync<TModel, T1, T2, TResult>(this IQueryable<TModel> queryable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Func<TModel, TResult> selector)
        {
            return CreateTreeDataSourceResultAsync(() =>
                QueryableExtensions.ToTreeDataSourceResult(queryable, request, idSelector, parentIDSelector, selector));
        }
  
        public static TreeDataSourceResult ToTreeDataSourceResult<TModel, T1, T2, TResult>(this IQueryable<TModel> queryable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            ModelStateDictionary modelState,
            Func<TModel, TResult> selector)
        {
            return queryable.CreateTreeDataSourceResult(request, idSelector, parentIDSelector, modelState, selector, null);
        }
  
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync<TModel, T1, T2, TResult>(this IQueryable<TModel> queryable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            ModelStateDictionary modelState,
            Func<TModel, TResult> selector)
        {
            return CreateTreeDataSourceResultAsync(() =>
                QueryableExtensions.ToTreeDataSourceResult(queryable, request, idSelector, parentIDSelector, modelState, selector));
        }
  
        public static TreeDataSourceResult ToTreeDataSourceResult<TModel, T1, T2, TResult>(this IQueryable<TModel> queryable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Expression<Func<TModel, bool>> rootSelector,
            ModelStateDictionary modelState,
            Func<TModel, TResult> selector)
        {
            return queryable.CreateTreeDataSourceResult(request, idSelector, parentIDSelector, modelState, selector, rootSelector);
        }
  
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync<TModel, T1, T2, TResult>(this IQueryable<TModel> queryable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Expression<Func<TModel, bool>> rootSelector,
            ModelStateDictionary modelState,
            Func<TModel, TResult> selector)
        {
            return CreateTreeDataSourceResultAsync(() =>
                QueryableExtensions.ToTreeDataSourceResult(queryable, request, idSelector, parentIDSelector, rootSelector, modelState, selector));
        }
  
        public static TreeDataSourceResult ToTreeDataSourceResult<TModel, T1, T2>(this IEnumerable<TModel> enumerable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector)
        {
            return enumerable.AsQueryable().CreateTreeDataSourceResult<TModel, T1, T2, TModel>(request, idSelector, parentIDSelector, null, null, null);
        }
  
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync<TModel, T1, T2>(this IEnumerable<TModel> enumerable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector)
        {
            return CreateTreeDataSourceResultAsync(() =>
                QueryableExtensions.ToTreeDataSourceResult(enumerable, request, idSelector, parentIDSelector));
        }
  
        public static TreeDataSourceResult ToTreeDataSourceResult<TModel, T1, T2>(this IEnumerable<TModel> enumerable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Expression<Func<TModel, bool>> rootSelector)
        {
            return enumerable.AsQueryable().CreateTreeDataSourceResult<TModel, T1, T2, TModel>(request, idSelector, parentIDSelector, null, null, rootSelector);
        }
  
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync<TModel, T1, T2>(this IEnumerable<TModel> enumerable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Expression<Func<TModel, bool>> rootSelector)
        {
            return CreateTreeDataSourceResultAsync(() =>
                QueryableExtensions.ToTreeDataSourceResult(enumerable, request, idSelector, parentIDSelector, rootSelector));
        }
  
        public static TreeDataSourceResult ToTreeDataSourceResult<TModel, T1, T2, TResult>(this IEnumerable<TModel> queryable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Expression<Func<TModel, bool>> rootSelector,
            Func<TModel, TResult> selector)
        {
            return queryable.AsQueryable().CreateTreeDataSourceResult(request, idSelector, parentIDSelector, null, selector, rootSelector);
        }
  
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync<TModel, T1, T2, TResult>(this IEnumerable<TModel> enumerable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Expression<Func<TModel, bool>> rootSelector,
            Func<TModel, TResult> selector)
        {
            return CreateTreeDataSourceResultAsync(() =>
                QueryableExtensions.ToTreeDataSourceResult(enumerable, request, idSelector, parentIDSelector, rootSelector));
        }
  
        public static TreeDataSourceResult ToTreeDataSourceResult<TModel, T1, T2>(this IEnumerable<TModel> queryable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            ModelStateDictionary modelState)
        {
            return queryable.AsQueryable().ToTreeDataSourceResult<TModel, T1, T2, TModel>(request, idSelector, parentIDSelector, modelState, null);
        }
  
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync<TModel, T1, T2>(this IEnumerable<TModel> enumerable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            ModelStateDictionary modelState)
        {
            return CreateTreeDataSourceResultAsync(() =>
                QueryableExtensions.ToTreeDataSourceResult(enumerable, request, idSelector, parentIDSelector, modelState));
        }
  
        public static TreeDataSourceResult ToTreeDataSourceResult<TModel, T1, T2>(this IEnumerable<TModel> enumerable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Expression<Func<TModel, bool>> rootSelector,
            ModelStateDictionary modelState)
        {
            return enumerable.AsQueryable().CreateTreeDataSourceResult<TModel, T1, T2, TModel>(request, idSelector, parentIDSelector, modelState, null, rootSelector);
        }
  
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync<TModel, T1, T2>(this IEnumerable<TModel> enumerable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Expression<Func<TModel, bool>> rootSelector,
            ModelStateDictionary modelState)
        {
            return CreateTreeDataSourceResultAsync(() =>
                QueryableExtensions.ToTreeDataSourceResult(enumerable, request, idSelector, parentIDSelector, rootSelector, modelState));
        }
  
        public static TreeDataSourceResult ToTreeDataSourceResult<TModel, T1, T2, TResult>(this IEnumerable<TModel> queryable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Func<TModel, TResult> selector)
        {
            return queryable.AsQueryable().CreateTreeDataSourceResult(request, idSelector, parentIDSelector, null, selector, null);
        }
  
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync<TModel, T1, T2, TResult>(this IEnumerable<TModel> enumerable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Func<TModel, TResult> selector)
        {
            return CreateTreeDataSourceResultAsync(() =>
                QueryableExtensions.ToTreeDataSourceResult(enumerable, request, idSelector, parentIDSelector, selector));
        }
  
        public static TreeDataSourceResult ToTreeDataSourceResult<TModel, T1, T2, TResult>(this IEnumerable<TModel> queryable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            ModelStateDictionary modelState,
            Func<TModel, TResult> selector)
        {
            return queryable.AsQueryable().CreateTreeDataSourceResult(request, idSelector, parentIDSelector, modelState, selector, null);
        }
  
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync<TModel, T1, T2, TResult>(this IEnumerable<TModel> enumerable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            ModelStateDictionary modelState,
            Func<TModel, TResult> selector)
        {
            return CreateTreeDataSourceResultAsync(() =>
                QueryableExtensions.ToTreeDataSourceResult(enumerable, request, idSelector, parentIDSelector, modelState, selector));
        }
  
        public static TreeDataSourceResult ToTreeDataSourceResult<TModel, T1, T2, TResult>(this IEnumerable<TModel> queryable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Expression<Func<TModel, bool>> rootSelector,
            ModelStateDictionary modelState,
            Func<TModel, TResult> selector)
        {
            return queryable.AsQueryable().CreateTreeDataSourceResult(request, idSelector, parentIDSelector, modelState, selector, rootSelector);
        }
  
        public static Task<TreeDataSourceResult> ToTreeDataSourceResultAsync<TModel, T1, T2, TResult>(this IEnumerable<TModel> enumerable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            Expression<Func<TModel, bool>> rootSelector,
            ModelStateDictionary modelState,
            Func<TModel, TResult> selector)
        {
            return CreateTreeDataSourceResultAsync(() =>
                QueryableExtensions.ToTreeDataSourceResult(enumerable, request, idSelector, parentIDSelector, rootSelector, modelState, selector));
        }
  
        private static Task<TreeDataSourceResult> CreateTreeDataSourceResultAsync(Func<TreeDataSourceResult> expression)
        {
            // Task.Run() is not available in .NET 4.0
            return Task.Factory.StartNew(expression, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
        }
  
        private static TreeDataSourceResult CreateTreeDataSourceResult<TModel, T1, T2, TResult>(this IQueryable queryable,
            DataSourceRequest request,
            Expression<Func<TModel, T1>> idSelector,
            Expression<Func<TModel, T2>> parentIDSelector,
            ModelStateDictionary modelState,
            Func<TModel, TResult> selector,
            Expression<Func<TModel, bool>> rootSelector)
        {
            var result = new TreeDataSourceResult();
  
            var data = queryable;
  
            var filters = new List<IFilterDescriptor>();
  
            if (request.Filters != null)
            {
                filters.AddRange(request.Filters);
            }
  
            if (filters.Any())
            {
                data = data.Where(filters);
  
                data = data.ParentsRecursive<TModel>(queryable, idSelector, parentIDSelector);
            }
  
            var filteredData = data;
  
            if (rootSelector != null)
            {
                data = data.Where(rootSelector);
            }
  
            var sort = new List<SortDescriptor>();
  
            if (request.Sorts != null)
            {
                sort.AddRange(request.Sorts);
            }
  
            var aggregates = new List<AggregateDescriptor>();
  
            if (request.Aggregates != null)
            {
                aggregates.AddRange(request.Aggregates);
            }
  
            if (aggregates.Any())
            {
                var dataSource = data;
                var groups = dataSource.GroupBy(parentIDSelector);
  
                foreach (IGrouping<T2, TModel> group in groups)
                {
                    result.AggregateResults.Add(Convert.ToString(group.Key), group.AggregateForLevel(filteredData, aggregates, idSelector, parentIDSelector));
                }
            }
  
            if (sort.Any())
            {
                data = data.Sort(sort);
            }
  
            result.Data = data.Execute(selector);
  
            if (modelState != null && !modelState.IsValid)
            {
                result.Errors = modelState.SerializeErrors();
            }
  
            return result;
        }
    }
}

Please let me know in case I can provide further assistance for this case.

Regards,
Svetlin
Progress Telerik
Try our brand new, jQuery-free Angular components built from ground-up which deliver the business app essential building blocks - a grid component, data visualization (charts) and form elements.
Frank
Top achievements
Rank 1
commented on 31 May 2021, 11:42 AM

Hi Svetlin, i am struggling with this exact issue word for word, and i am struggling to understand how else the server side 'paging' would work without the `skip`. I see the code above but it would have been nice to have this functionality work out of the box. The example you gave further up makes use of the skip value, but for some reason the skip parameter is not sent to the api, other than being wasteful (i see page one being requested multiple times) the method does not allow me to make any form of change
Tags
General Discussions
Asked by
Yuriy
Top achievements
Rank 1
Answers by
Svet
Telerik team
Yuriy
Top achievements
Rank 1
Share this question
or