-
Notifications
You must be signed in to change notification settings - Fork 140
Open
Description
Hello everyone.
The project I'm working on was using version 1.0.0-beta and I'm trying to upgrade to 1.1.0. Looks like a small change in PropertyModelReferece
is causing the exception at the bottom when processing a typecasting filter.
- In short, the model is an entity
Child
that extendsParent
that has anId
property. - The problem happens on requests with filters like
/Parent$filter=Child/Id eq 1
(filter forParent
that areChild
withId = 1
right?)- Removing the
Child
part works fine:Parent$filter=Id eq 1
. - There's are better ways to do this query, but I don't control the clients and this is just an example.
- Removing the
- Expanding
Child
properties that are expandable works fine. - The source is a database using Entity Framework, and I can see the database query works and returns all
Parent
entities. - .NET Framework 4.8.
Am I using Restier incorrectly or is this an oversight? I don't know exactly what other details I can provide, but let me know. In any case, can it be fixed in my code somehow?
Thanks!
Here's what I could find by debugging the code, but I don't know much about Restier internals so feel free to ignore:
- The exception happens in
QueryExpressionContext.ComputeMemberModelReference
when processing aTypeAs
expression. The methodGetModelReferenceForNode
returns null, and creating aPropertyModelReference
then throws becausesource
is null. - The difference is that in 1.0.0-rc1 it uses the
PropertyModelReference
constructor that doesn't null-checksource
, while in newer versions it always checks. - Going a little further, I noticed that the filter becomes a Linq expression like
GetQueryableSource("Parent", null).Where($it => ($it As Child).Id == 1)
. Linq'sExpressionVisitor.VisitLambda
processes the lambda body before the lambda parameters.GetModelReferenceForNode
tries to get the expression for$it
from the model references cache, but doesn't find it, returning null. Using the debugger, I forced it to visit the parameters first, which adds$it
to the cache, and it worked somehow.
System.ArgumentNullException: Value cannot be null.
Parameter name: source
at void Ensure.NotNull<T>(T value, string paramName)
at new Microsoft.Restier.Core.Query.PropertyModelReference(QueryModelReference source, string propertyName) x 2
at QueryModelReference Microsoft.Restier.Core.Query.QueryExpressionContext.ComputeMemberModelReference(MemberExpression member)
at QueryModelReference Microsoft.Restier.Core.Query.QueryExpressionContext.ComputeModelReference()
at void Microsoft.Restier.Core.Query.QueryExpressionContext.UpdateModelReference()
at void Microsoft.Restier.Core.Query.QueryExpressionContext.PushVisitedNode(Expression visitedNode)
at Expression Microsoft.Restier.Core.Query.DefaultQueryHandler+QueryExpressionVisitor.Visit(Expression node)
at Expression System.Linq.Expressions.ExpressionVisitor.VisitBinary(BinaryExpression node)
at Expression System.Linq.Expressions.BinaryExpression.Accept(ExpressionVisitor visitor)
at Expression System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Expression Microsoft.Restier.Core.Query.DefaultQueryHandler+QueryExpressionVisitor.Visit(Expression node)
at Expression System.Linq.Expressions.ExpressionVisitor.VisitLambda<T>(Expression<T> node)
at Expression System.Linq.Expressions.Expression<TDelegate>.Accept(ExpressionVisitor visitor)
at Expression System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Expression Microsoft.Restier.Core.Query.DefaultQueryHandler+QueryExpressionVisitor.Visit(Expression node)
at Expression System.Linq.Expressions.ExpressionVisitor.VisitUnary(UnaryExpression node)
at Expression System.Linq.Expressions.UnaryExpression.Accept(ExpressionVisitor visitor)
at Expression System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Expression Microsoft.Restier.Core.Query.DefaultQueryHandler+QueryExpressionVisitor.Visit(Expression node)
at Expression[] System.Linq.Expressions.ExpressionVisitor.VisitArguments(IArgumentProvider nodes)
at Expression System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
at Expression System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at Expression System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Expression Microsoft.Restier.Core.Query.DefaultQueryHandler+QueryExpressionVisitor.Visit(Expression node)
at async Task<QueryResult> Microsoft.Restier.Core.Query.DefaultQueryHandler.QueryAsync(QueryContext context, CancellationToken cancellationToken)
at async Task<QueryResult> Microsoft.Restier.Core.ApiBaseExtensions.QueryAsync(ApiBase api, QueryRequest request, CancellationToken cancellationToken)
at async Task<IQueryable> Microsoft.Restier.AspNet.RestierController.ExecuteQuery(IQueryable queryable, CancellationToken cancellationToken)
at async Task<HttpResponseMessage> Microsoft.Restier.AspNet.RestierController.Get(CancellationToken cancellationToken)
at async Task<object> System.Threading.Tasks.TaskHelpersExtensions.CastToObject<T>(Task<T> task)
at async Task<HttpResponseMessage> System.Web.Http.Controllers.ApiControllerActionInvoker.InvokeActionAsyncCore(HttpActionContext actionContext, CancellationToken cancellationToken)
at async Task<HttpResponseMessage> System.Web.Http.Controllers.ActionFilterResult.ExecuteAsync(CancellationToken cancellationToken)
at async Task<HttpResponseMessage> System.Web.Http.Filters.AuthorizationFilterAttribute.ExecuteAuthorizationFilterAsyncCore(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
at async Task<HttpResponseMessage> System.Web.Http.Controllers.ExceptionFilterResult.ExecuteAsync(CancellationToken cancellationToken)
Metadata
Metadata
Assignees
Labels
No labels