Изменение дерева выражений в OData API - PullRequest
0 голосов
/ 08 февраля 2019

Я пытаюсь изменить дерево выражений запроса OData, прежде чем он достигнет базы данных.Так как мой ExpressionVisitor относится к запросу, я не могу использовать метод ParseQuery в QueryModelGenerator, чтобы изменить его.Поскольку я заранее не знаю тип дерева выражений (это может быть SelectExpandBinder SelectSome, SelectExpandBinder SelectAllAndExpand, ...), я должен использовать отражение для вызова метода CreateQuery.Я не уверен, что это правильный подход.

    [EnableQuery(MaxExpansionDepth = 0)]
    public new ActionResult<Product> Get(ODataQueryOptions<Product> odataQueryOptions)
    {
        var get = this.dbContext.Products;
        var getWithOdataQueryOptions = odataQueryOptions.ApplyTo(get);

        var expression = this.productVisitor.Visit(getWithOdataQueryOptions.Expression);

        // Not sure if this is the right way.
        var method = typeof(IQueryProvider).GetMethods().Where(w => w.Name == "CreateQuery" && w.IsGenericMethod).First();
        var generic = method.MakeGenericMethod(expression.Type.GenericTypeArguments[0]);

        var result = generic.Invoke(getWithOdataQueryOptions.Provider, new[] { expression });

        return this.Ok(result);
    }

Этот подход отлично подходит для запросов OData без $ expand или $ select.Но как только я запрашиваю что-то вроде / Products? $ Select = Id, я получаю следующее исключение:

{
    "error": {
        "code": "",
        "message": "The query specified in the URI is not valid. Could not find a property named 'Id' on type 'Microsoft.AspNet.OData.Query.Expressions.SelectSome_1OfProduct'.",
        "details": [],
        "innererror": {
            "message": "Could not find a property named 'id' on type 'Microsoft.AspNet.OData.Query.Expressions.SelectSome_1OfProduct'.",
            "type": "Microsoft.OData.ODataException",
            "stacktrace": "   at Microsoft.OData.UriParser.SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(PathSegmentToken tokenIn, IEdmModel model, IEdmStructuredType edmType, ODataUriResolver resolver)\r\n   at Microsoft.OData.UriParser.SelectPropertyVisitor.ProcessTokenAsPath(NonSystemToken tokenIn)\r\n   at Microsoft.OData.UriParser.SelectPropertyVisitor.Visit(NonSystemToken tokenIn)\r\n   at Microsoft.OData.UriParser.SelectBinder.Bind(SelectToken tokenIn)\r\n   at Microsoft.OData.UriParser.SelectExpandBinder.Bind(ExpandToken tokenIn)\r\n   at Microsoft.OData.UriParser.SelectExpandSemanticBinder.Bind(ODataPathInfo odataPathInfo, ExpandToken expandToken, SelectToken selectToken, ODataUriParserConfiguration configuration)\r\n   at Microsoft.OData.UriParser.ODataQueryOptionParser.ParseSelectAndExpandImplementation(String select, String expand, ODataUriParserConfiguration configuration, ODataPathInfo odataPathInfo)\r\n   at Microsoft.OData.UriParser.ODataQueryOptionParser.ParseSelectAndExpand()\r\n   at Microsoft.AspNet.OData.Query.Validators.SelectExpandQueryValidator.Validate(SelectExpandQueryOption selectExpandQueryOption, ODataValidationSettings validationSettings)\r\n   at Microsoft.AspNet.OData.Query.Validators.ODataQueryValidator.Validate(ODataQueryOptions options, ODataValidationSettings validationSettings)\r\n   at Microsoft.AspNet.OData.EnableQueryAttribute.ValidateQuery(HttpRequest request, ODataQueryOptions queryOptions)\r\n   at Microsoft.AspNet.OData.EnableQueryAttribute.<>c__DisplayClass1_0.<OnActionExecuted>b__1(ODataQueryContext queryContext)\r\n   at Microsoft.AspNet.OData.EnableQueryAttribute.ExecuteQuery(Object responseValue, IQueryable singleResultCollection, IWebApiActionDescriptor actionDescriptor, Func`2 modelFunction, IWebApiRequestMessage request, Func`2 createQueryOptionFunction)\r\n   at Microsoft.AspNet.OData.EnableQueryAttribute.OnActionExecuted(Object responseValue, IQueryable singleResultCollection, IWebApiActionDescriptor actionDescriptor, IWebApiRequestMessage request, Func`2 modelFunction, Func`2 createQueryOptionFunction, Action`1 createResponseAction, Action`3 createErrorAction)"
        }
    }
}

Как вы можете видеть из приведенного ниже кода, я фактически пытался полностью и просто удалить своего посетителя.создать новый IQueryable на основе дерева выражений, не меняя его, и я получаю тот же результат.

    var get = this.dbContext.Products;
    var getWithOdataQueryOptions = odataQueryOptions.ApplyTo(get);

    // Not sure if this is the right way.
    var method = typeof(IQueryProvider).GetMethods().Where(w => w.Name == "CreateQuery" && w.IsGenericMethod).First();
    var generic = method.MakeGenericMethod(getWithOdataQueryOptions.Expression.Type.GenericTypeArguments[0]);

    var result = generic.Invoke(getWithOdataQueryOptions.Provider, new[] { getWithOdataQueryOptions.Expression });

    return this.Ok(result);

Как я могу изменить дерево выражений для определенного контекста (то есть без использования QueryModelGenerator) без потери ODataфункциональность?

...