OData / AspNetCoreOData

ASP.NET Core OData: A server library built upon ODataLib and ASP.NET Core
Other
457 stars 158 forks source link

$apply when combined with /groupby and $top not functioning #969

Closed JuanGoDev closed 1 year ago

JuanGoDev commented 1 year ago

OData Version: Microsoft.AspNetCore.OData - 8.2.0

.Net 6.0

Previously, I was using Microsoft.AspNetCore.OData version 8.0.12, and when combining the $apply query option with the /groupby transformation and the $orderby clause, I was encountering the following exception:

System.ArgumentException: Instance property 'Values' is not defined for type 'System.Object' (Parameter 'propertyName')
   at System.Linq.Expressions.Expression.Property(Expression expression, String propertyName)
   at Microsoft.AspNetCore.OData.Query.Expressions.QueryBinder.GetPropertyExpression(Expression source, String propertyPath, Boolean isAggregated)
   at Microsoft.AspNetCore.OData.Query.Expressions.QueryBinder.CreatePropertyAccessExpression(Expression source, QueryBinderContext context, IEdmProperty property, String propertyPath)
   at Microsoft.AspNetCore.OData.Query.Expressions.QueryBinder.BindPropertyAccessQueryNode(SingleValuePropertyAccessNode propertyAccessNode, QueryBinderContext context)
   at Microsoft.AspNetCore.OData.Query.Expressions.OrderByBinder.BindOrderBy(OrderByClause orderByClause, QueryBinderContext context)
   at Microsoft.AspNetCore.OData.Query.Expressions.BinderExtensions.ApplyBind(IOrderByBinder binder, IQueryable query, OrderByClause orderByClause, QueryBinderContext context, Boolean alreadyOrdered)
   at Microsoft.AspNetCore.OData.Query.OrderByQueryOption.AddOrderByQueryForProperty(IOrderByBinder orderByBinder, OrderByClause orderbyClause, IQueryable querySoFar, QueryBinderContext binderContext, Boolean alreadyOrdered)
   at Microsoft.AspNetCore.OData.Query.OrderByQueryOption.ApplyToCore(IQueryable query, ODataQuerySettings querySettings)
   at Microsoft.AspNetCore.OData.Query.ODataQueryOptions.ApplyTo(IQueryable query, ODataQuerySettings querySettings)
   at Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.ExecuteQuery(Object responseValue, IQueryable singleResultCollection, ControllerActionDescriptor actionDescriptor, HttpRequest request)
   at Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.OnActionExecuted(ActionExecutedContext actionExecutedContext, Object responseValue, IQueryable singleResultCollection, ControllerActionDescriptor actionDescriptor, HttpRequest request)
   at Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.OnActionExecuted(ActionExecutedContext actionExecutedContext)
   at Microsoft.AspNetCore.Mvc.Filters.ActionFilterAttribute.OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context)
   at NSwag.AspNetCore.Middlewares.SwaggerUiIndexMiddleware.Invoke(HttpContext context)
   at NSwag.AspNetCore.Middlewares.RedirectToIndexMiddleware.Invoke(HttpContext context)
   at NSwag.AspNetCore.Middlewares.OpenApiDocumentMiddleware.Invoke(HttpContext context)
   at NWebsec.AspNetCore.Middleware.Middleware.MiddlewareBase.Invoke(HttpContext context)
   at NWebsec.AspNetCore.Middleware.Middleware.MiddlewareBase.Invoke(HttpContext context)
   at NWebsec.AspNetCore.Middleware.Middleware.MiddlewareBase.Invoke(HttpContext context)
   at NWebsec.AspNetCore.Middleware.Middleware.MiddlewareBase.Invoke(HttpContext context)
   at NWebsec.AspNetCore.Middleware.Middleware.MiddlewareBase.Invoke(HttpContext context)
   at NWebsec.AspNetCore.Middleware.Middleware.MiddlewareBase.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

This exception was fixed by updating the Microsoft.AspNetCore.OData library to version 8.2.0.

However, in the structure of my OData query, I am using the $top clause, which is generating the same exception as before.

System.ArgumentException: Instance property 'Values' is not defined for type 'System.Object' (Parameter 'propertyName')
   at System.Linq.Expressions.Expression.Property(Expression expression, String propertyName)
   at Microsoft.AspNetCore.OData.Query.Expressions.QueryBinder.GetPropertyExpression(Expression source, String propertyPath, Boolean isAggregated)
   at Microsoft.AspNetCore.OData.Query.Expressions.QueryBinder.CreatePropertyAccessExpression(Expression source, QueryBinderContext context, IEdmProperty property, String propertyPath)
   at Microsoft.AspNetCore.OData.Query.Expressions.QueryBinder.BindPropertyAccessQueryNode(SingleValuePropertyAccessNode propertyAccessNode, QueryBinderContext context)
   at Microsoft.AspNetCore.OData.Query.Expressions.OrderByBinder.BindOrderBy(OrderByClause orderByClause, QueryBinderContext context)
   at Microsoft.AspNetCore.OData.Query.Expressions.BinderExtensions.ApplyBind(IOrderByBinder binder, IQueryable query, OrderByClause orderByClause, QueryBinderContext context, Boolean alreadyOrdered)
   at Microsoft.AspNetCore.OData.Query.OrderByQueryOption.AddOrderByQueryForProperty(IOrderByBinder orderByBinder, OrderByClause orderbyClause, IQueryable querySoFar, QueryBinderContext binderContext, Boolean alreadyOrdered)
   at Microsoft.AspNetCore.OData.Query.OrderByQueryOption.ApplyToCore(IQueryable query, ODataQuerySettings querySettings)
   at Microsoft.AspNetCore.OData.Query.ODataQueryOptions.ApplyTo(IQueryable query, ODataQuerySettings querySettings)
   at Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.ExecuteQuery(Object responseValue, IQueryable singleResultCollection, ControllerActionDescriptor actionDescriptor, HttpRequest request)
   at Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.OnActionExecuted(ActionExecutedContext actionExecutedContext, Object responseValue, IQueryable singleResultCollection, ControllerActionDescriptor actionDescriptor, HttpRequest request)
   at Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.OnActionExecuted(ActionExecutedContext actionExecutedContext)
   at Microsoft.AspNetCore.Mvc.Filters.ActionFilterAttribute.OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context)
   at NSwag.AspNetCore.Middlewares.SwaggerUiIndexMiddleware.Invoke(HttpContext context)
   at NSwag.AspNetCore.Middlewares.RedirectToIndexMiddleware.Invoke(HttpContext context)
   at NSwag.AspNetCore.Middlewares.OpenApiDocumentMiddleware.Invoke(HttpContext context)
   at NWebsec.AspNetCore.Middleware.Middleware.MiddlewareBase.Invoke(HttpContext context)
   at NWebsec.AspNetCore.Middleware.Middleware.MiddlewareBase.Invoke(HttpContext context)
   at NWebsec.AspNetCore.Middleware.Middleware.MiddlewareBase.Invoke(HttpContext context)
   at NWebsec.AspNetCore.Middleware.Middleware.MiddlewareBase.Invoke(HttpContext context)
   at NWebsec.AspNetCore.Middleware.Middleware.MiddlewareBase.Invoke(HttpContext context)
   at NWebsec.AspNetCore.Middleware.Middleware.MiddlewareBase.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Example Query: $apply=filter((elementId%20eq%20375895)%20and%20(contains(node/name,%27A%27)))/groupby((node/nodeid,%20node/name))&$orderby=node/name&$top=70&$count=true

This is an obstacle that my team is facing when implementing OData in version 8.2.0 because without the $top clause in our OData query, it would not be optimal.

In conclusion, the error is occurring when using the $top clause combined with the /groupby transformation within the $apply clause.

corranrogue9 commented 1 year ago

Thanks, let me work with @ElizabethOkerio to see if we can get $top addressed as well.

JuanGoDev commented 1 year ago

Gracias, déjame trabajar con@ElizabethOkeriopara ver si podemos ser $topatendidos también.

Perfect, I'm attentive!

ElizabethOkerio commented 1 year ago

@JuanGoDev The issue here is the use of $filter with $apply and all the other query options and that was fixed here: https://github.com/OData/AspNetCoreOData/pull/940. This is yet to be released. You can test this out and let us know whether it works as we prepare to do a release.

JuanGoDev commented 1 year ago

@JuanGoDev El problema aquí es el uso de $filter con $apply y todas las demás opciones de consulta y eso se solucionó aquí: #940 . Esto aún no se ha publicado. Puede probar esto y decirnos si funciona mientras nos preparamos para hacer un lanzamiento.

@ElizabethOkerio related to #940 , did not work for me, $apply combined with /groupby and $filter does work, but when I add the $top the error persists.

ElizabethOkerio commented 1 year ago

Ok. will take a look.

JuanGoDev commented 1 year ago

@ElizabethOkerio @xuzhg Hi Elizabeth Okerio, first of all thank you for your help and the help of the OData team.

After upgrading the OData version to 8.2.1 I started to experience another error, which as far as I have been able to find out is related to the $expand option, I will tell you what is happening to me:

Example of OData paths with which I am experiencing error:

http://localhost:49350/odata/v1/categoryelements?$expand=category

https://localhost:49350/v1/odata/categoryelements?$expand=category,icon&$top=10

image

When generating the response to the OData query, the JSON values are generated incomplete, and therefore my API generates error when deserializing the response.

Among other paths in which $expand is used.

But the funny thing is that I had as a premise that this happened with my OData paths where I implemented $expand, but it didn't, for example the following OData path worked for me:

https://localhost:49350/v1/odata/nodeconnections?$expand=SourceNode($select=name,%20isActive),DestinationNode($select=name,%20isActive),Algorithm&$select=nodeConnectionId,sourceNodeId,destinationNodeId,controlLimit,isActive,isTransfer,algorithmId,rowVersion&$top=10&$orderby=nodeConnectionId%20desc.

Can you help me with some information to let me know if this error is due to some change in the edm model, or what may be happening in my case, thank you very much for your help.