Skip to content

System.ArgumentNullException: Value cannot be null. Parameter name: source #771

@hossomi-baxenergy

Description

@hossomi-baxenergy

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 extends Parent that has an Id property.
  • The problem happens on requests with filters like /Parent$filter=Child/Id eq 1 (filter for Parent that are Child with Id = 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.
  • 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 a TypeAs expression. The method GetModelReferenceForNode returns null, and creating a PropertyModelReference then throws because source is null.
  • The difference is that in 1.0.0-rc1 it uses the PropertyModelReference constructor that doesn't null-check source, 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's ExpressionVisitor.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

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions