public IPaginationTransformResult <T> ApplyPagination <T>(IQueryable <T> query, HttpRequestMessage request)
        {
            var hasPageNumberParam = false;
            var hasPageSizeParam   = false;
            var pageNumber         = 0;
            var pageSize           = _maxPageSize ?? DefaultPageSize;

            foreach (var kvp in request.GetQueryNameValuePairs())
            {
                if (kvp.Key == PageNumberQueryParam)
                {
                    hasPageNumberParam = true;
                    if (!int.TryParse(kvp.Value, out pageNumber))
                    {
                        throw JsonApiException.CreateForParameterError("Invalid page number",
                                                                       "Page number must be a positive integer.", PageNumberQueryParam);
                    }
                }
                else if (kvp.Key == PageSizeQueryParam)
                {
                    hasPageSizeParam = true;
                    if (!int.TryParse(kvp.Value, out pageSize))
                    {
                        throw JsonApiException.CreateForParameterError("Invalid page size",
                                                                       "Page size must be a positive integer.", PageSizeQueryParam);
                    }
                }
            }

            if (!hasPageNumberParam && !hasPageSizeParam)
            {
                return(new DefaultPaginationTransformResult <T>
                {
                    PagedQuery = query,
                    PaginationWasApplied = false
                });
            }

            if ((hasPageNumberParam && !hasPageSizeParam))
            {
                throw JsonApiException.CreateForParameterError("Page size missing",
                                                               string.Format("In order for paging to work properly, if either {0} or {1} is set, both must be.",
                                                                             PageNumberQueryParam, PageSizeQueryParam), PageNumberQueryParam);
            }

            if (pageNumber < 0)
            {
                throw JsonApiException.CreateForParameterError("Page number out of bounds",
                                                               "Page number must not be negative.", PageNumberQueryParam);
            }

            if (pageSize <= 0)
            {
                throw JsonApiException.CreateForParameterError("Page size out of bounds",
                                                               "Page size must be greater than or equal to 1.", PageSizeQueryParam);
            }

            if (_maxPageSize != null && pageSize > _maxPageSize.Value)
            {
                pageSize = _maxPageSize.Value;
            }

            if (pageNumber > 0)
            {
                pageNumber -= 1; // pagination is 1 based in frontend but zero based in backend!
            }

            var skip = pageNumber * pageSize;

            return(new DefaultPaginationTransformResult <T>
            {
                PageNumber = pageNumber,
                PageSize = pageSize,
                PagedQuery = query.Skip(skip).Take(pageSize),
                PaginationWasApplied = true
            });
        }
        public IOrderedQueryable <T> Sort <T>(IQueryable <T> query, string[] sortExpressions)
        {
            if (sortExpressions == null || sortExpressions.Length == 0)
            {
                sortExpressions = new [] { "id" }
            }
            ;

            var selectors      = new List <ISelector <T> >();
            var usedProperties = new Dictionary <PropertyInfo, object>();

            var registration = _resourceTypeRegistry.GetRegistrationForType(typeof(T));

            foreach (var sortExpression in sortExpressions)
            {
                if (string.IsNullOrEmpty(sortExpression))
                {
                    throw JsonApiException.CreateForParameterError("Empty sort expression", "One of the sort expressions is empty.", "sort");
                }

                bool   ascending;
                string fieldName;
                if (sortExpression[0] == '-')
                {
                    ascending = false;
                    fieldName = sortExpression.Substring(1);
                }
                else
                {
                    ascending = true;
                    fieldName = sortExpression;
                }

                if (string.IsNullOrWhiteSpace(fieldName))
                {
                    throw JsonApiException.CreateForParameterError("Empty sort expression", "One of the sort expressions is empty.", "sort");
                }

                var        paramExpr = Expression.Parameter(typeof(T));
                Expression sortValueExpression;

                if (fieldName == "id")
                {
                    sortValueExpression = registration.GetSortByIdExpression(paramExpr);
                }
                else
                {
                    var modelProperty = registration.GetFieldByName(fieldName);
                    if (modelProperty == null)
                    {
                        throw JsonApiException.CreateForParameterError("Attribute not found",
                                                                       string.Format("The attribute \"{0}\" does not exist on type \"{1}\".",
                                                                                     fieldName, registration.ResourceTypeName), "sort");
                    }

                    var property = modelProperty.Property;

                    if (usedProperties.ContainsKey(property))
                    {
                        throw JsonApiException.CreateForParameterError("Attribute specified more than once",
                                                                       string.Format("The attribute \"{0}\" was specified more than once.", fieldName), "sort");
                    }

                    usedProperties[property] = null;
                    sortValueExpression      = Expression.Property(paramExpr, property);
                }

                var selector = GetSelector <T>(paramExpr, sortValueExpression, !ascending);
                selectors.Add(selector);
            }

            var firstSelector = selectors.First();

            IOrderedQueryable <T> workingQuery = firstSelector.ApplyInitially(query);

            return(selectors.Skip(1).Aggregate(workingQuery, (current, selector) => selector.ApplySubsequently(current)));
        }