question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

multiple orderby throws exception

See original GitHub issue

Opened on SO as well: stack overflow

When trying to execute something like /odata/roles?$orderby=IsActive,Name I get the below exception.

"No generic method ‘ThenBy’ on type ‘System.Linq.Queryable’ is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic. "

"System.InvalidOperationException: No generic method 'ThenBy' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic. at System.Linq.Expressions.Expression.FindMethod(Type type, String methodName, Type[] typeArgs, Expression[] args, BindingFlags flags) at System.Linq.Expressions.Expression.Call(Type type, String methodName, Type[] typeArguments, Expression[] arguments) at AutoMapper.AspNet.OData.LinqExtensions.GetOrderByMethod[T](ODataQueryOptions1 options, Expression expression) at AutoMapper.AspNet.OData.LinqExtensions.GetQueryableExpression[T](ODataQueryOptions1 options) at AutoMapper.AspNet.OData.QueryableExtensions.GetAsync[TModel,TData](IQueryable1 query, IMapper mapper, ODataQueryOptions1 options, HandleNullPropagationOption handleNullPropagation) at Mpi.PlanningApp.Web.Features.Roles.RolesController.Get(ODataQueryOptions1 options) in C:\Users\A330429\Source\Repos\VVC\Planning App\src\Mpi.PlanningApp.Web\Features\Roles\RolesController.cs:line 52 at Microsoft.AspNetCore.Mvc.Internal.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at System.Threading.Tasks.ValueTask1.get_Result() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeActionMethodAsync() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context) at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync() at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext) at Hellang.Middleware.ProblemDetails.ProblemDetailsMiddleware.Invoke(HttpContext context)"

Posted more code examples on stack overflow. If it’s needed, I can copy it to here.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:15 (15 by maintainers)

github_iconTop GitHub Comments

1reaction
xaviervvcommented, Jan 31, 2020

Sounds good. I will create a pr with the current fix tonight with the smaller methods you provided. I tested that, and works as it should now.

Get Outlook for Androidhttps://aka.ms/ghei36


From: Blaise Taylor notifications@github.com Sent: Friday, January 31, 2020 11:42:05 AM To: AutoMapper/AutoMapper.Extensions.OData AutoMapper.Extensions.OData@noreply.github.com Cc: Xavier tuffen_91@hotmail.com; Author author@noreply.github.com Subject: Re: [AutoMapper/AutoMapper.Extensions.OData] multiple orderby throws exception (#3)

The problem with paging is that we handle paging by generating the Queryable.Skip().Take() expression but OData also queries the result i.e. you have two records in the database. Our query skips the first one and OData’s EnableQuery skips the second one. Remove [EnableQuery()] from your controller method for paging to work. I’ll create 3 separate issues for:

  • Handling CountQueryOption without OData’s EnableQuery.
  • Handling selection of a subset of fields on the server.
  • Mention this issue of possible double evaluation because of EnableQuery.

We’ll only address the ThenBy() bug in this issue.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/AutoMapper/AutoMapper.Extensions.OData/issues/3?email_source=notifications&email_token=AIMW3QV5EHPTFKXHGGWD5ZLRAP573A5CNFSM4KMU3IWKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEKOIERA#issuecomment-580682308, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AIMW3QTQBJTU5WSKQUVK4XLRAP573ANCNFSM4KMU3IWA.

1reaction
BlaiseDcommented, Jan 29, 2020

This is nice. If you plan to create a PR consider shorter methods like below.

You won’t need the appyTo (you’re creating the whole lambda expression from scratch). Also no need for ParameterExpression arg. That already lives in GetQueryableExpression<T>. I don’t know if OData supports sorting on child properties of references but hence the GetMemberInfoFromFullName below.

        public static MethodCallExpression GetOrderByMethod<T>(this ODataQueryOptions<T> options, Expression expression)
        {
            if (options.OrderBy == null && options.Top == null)
                return null;

            if (options.OrderBy == null)
            {
                return Expression.Call
                (
                    typeof(Queryable),
                    "Take",
                    new[] { typeof(T) }, expression, Expression.Constant(options.Top.Value)
                );
            }

            //IQueryable queryable = Enumerable.Empty<T>().AsQueryable();
            //queryable = options.OrderBy.ApplyTo(queryable, new ODataQuerySettings());
            //MethodCallExpression mce = (MethodCallExpression)queryable.Expression;

            //mce = Expression.Call(typeof(Queryable), mce.Method.Name, new Type[] { typeof(T), mce.Arguments[1].GetReturnType() }, expression, mce.Arguments[1]);

            //if (options.Skip != null)
            //    mce = Expression.Call(typeof(Queryable), "Skip", new[] { typeof(T) }, mce, Expression.Constant(options.Skip.Value));
            //if (options.Top != null)
            //    mce = Expression.Call(typeof(Queryable), "Take", new[] { typeof(T) }, mce, Expression.Constant(options.Top.Value));

            //return mce;

            return options.OrderBy.OrderByNodes.Aggregate(null, (MethodCallExpression mce, OrderByNode orderByNode) =>
            {
                OrderByPropertyNode propertyNode = (OrderByPropertyNode)orderByNode;
                return mce == null
                    ? expression.GetOrderByCall(propertyNode.Property.Name, orderByNode.Direction)
                    : mce.GetThenByCall(propertyNode.Property.Name, orderByNode.Direction);
            })
            .GetSkipCall(options.Skip)
            .GetTakeCall(options.Top);
        }

        public static MethodCallExpression GetSkipCall(this MethodCallExpression expression, SkipQueryOption skip)
        {
            if (skip == null) return expression;

            return Expression.Call
            (
                typeof(Queryable),
                "Skip",
                new[] { expression.GetUnderlyingElementType() },
                expression,
                Expression.Constant(skip.Value)
            );
        }

        public static MethodCallExpression GetTakeCall(this MethodCallExpression expression, TopQueryOption top)
        {
            if (top == null) return expression;

            return Expression.Call
            (
                typeof(Queryable),
                "Take",
                new[] { expression.GetUnderlyingElementType() },
                expression,
                Expression.Constant(top.Value)
            );
        }

        public static MethodCallExpression GetOrderByCall(this Expression expression, string memberFullName, OrderByDirection sortDirection, string selectorParameterName = "a")
        {
            Type sourceType = expression.GetUnderlyingElementType();
            MemberInfo memberInfo = sourceType.GetMemberInfoFromFullName(memberFullName);
            return Expression.Call
            (
                typeof(Queryable),
                sortDirection == OrderByDirection.Ascending ? "OrderBy" : "OrderByDescending",
                new Type[] { sourceType, memberInfo.GetMemberType() },
                expression,
                memberFullName.GetTypedSelector(sourceType, selectorParameterName)
            );
        }

        public static MethodCallExpression GetThenByCall(this MethodCallExpression expression, string memberFullName, OrderByDirection sortDirection, string selectorParameterName = "a")
        {
            Type sourceType = expression.GetUnderlyingElementType();
            MemberInfo memberInfo = sourceType.GetMemberInfoFromFullName(memberFullName);
            return Expression.Call
            (
                typeof(Queryable),
                sortDirection == OrderByDirection.Ascending ? "ThenBy" : "ThenByDescending",
                new Type[] { sourceType, memberInfo.GetMemberType() },
                expression,
                memberFullName.GetTypedSelector(sourceType, selectorParameterName)
            );
        }

        public static Type GetUnderlyingElementType(this Expression expression)
            => expression.Type.GetUnderlyingElementType();

        public static MemberInfo GetMemberInfoFromFullName(this Type type, string propertyFullName)
        {
            if (propertyFullName.IndexOf('.') < 0)
            {
                return type.GetMemberInfo(propertyFullName);
            }

            string propertyName = propertyFullName.Substring(0, propertyFullName.IndexOf('.'));
            string childFullName = propertyFullName.Substring(propertyFullName.IndexOf('.') + 1);

            return GetMemberInfoFromFullName(type.GetMemberInfo(propertyName).GetMemberType(), childFullName);
        }
Read more comments on GitHub >

github_iconTop Results From Across the Web

Adding multiple ORDER BY clauses to an NHibernate ...
This throws the exception: NHibernate.Hql.Ast.ANTLR.QuerySyntaxException was unhandled HResult=-2146232832 Message=Exception of type ...
Read more >
OrderBy on column 2 or more navigation properties away ...
Include throws exception #3811 ... OrderBy must be ordering by a column at least 2 navigation properties away from the base table.
Read more >
Multiple "OrderBy" calls should not be used
There's no point in chaining multiple OrderBy calls in a LINQ; only the last one will be reflected in the result because each...
Read more >
Using Multiple Order By Clause in LINQ and Lambda ...
The orderby keyword can be used in LINQ to retrieve the ordered list of records based on the specified field. ... select m).ToList();....
Read more >
Sorting in C#: OrderBy.OrderBy or OrderBy.ThenBy? ...
Suppose we need to sort the collection by multiple keys. In C#, we can do this with the help of OrderBy().OrderBy() or OrderBy().ThenBy()....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found