查询具有可为空的datetimetimeoffset属性的链接实体时出现问题

时间:2015-04-20 13:34:31

标签: c# odata odata-v4 simple.odata

我正在使用simple.odata.client v4来访问我的odata rest api。数据模型非常复杂。我遇到的问题是我只想获得相关实体满足涉及可以为null的属性datetimeoffset(OnHandLastUpdated)的条件的实体。实际上在api方面它是一个可以为空的日期时间,但我认为odata v4会自动转换它。我尝试运行的代码是:

        var items =
oDataClient.For<ClientProductSku>()
    .Filter(x => x.ClientId == clientId && x.Product.SupplierProductSkuClient
        .All(y => y.SupplierProductSku.SupplierProductSkuOnHand
            .Any(z => z.OnHandLastUpdated.HasValue && z.OnHandLastUpdated.Value > DateTimeOffset.Now.AddMinutes(-5))))
    .Expand(UpdateSkuOnhandExpandTables)
    .FindEntriesAsync(annotations)
    .Result;

我得到的错误如下:

  "error":{
    "code":"","message":"The query specified in the URI is not valid. Can only bind segments that are Navigation, Structural, Complex, or Collections. We found a segment 'OnHandLastUpdated' that isn't any of those. Please revise the query.","innererror":{
      "message":"Can only bind segments that are Navigation, Structural, Complex, or Collections. We found a segment 'OnHandLastUpdated' that isn't any of those. Please revise the query.","type":"Microsoft.OData.Core.ODataException","stacktrace":"   at Microsoft.OData.Core.UriParser.Parsers.InnerPathTokenBinder.BindInnerPathSegment(InnerPathToken segmentToken)\r\n   at Microsoft.OData.Core.UriParser.Parsers.MetadataBinder.BindInnerPathSegment(InnerPathToken token)\r\n   at Microsoft.OData.Core.UriParser.Parsers.MetadataBinder.Bind(QueryToken token)\r\n   at Microsoft.OData.Core.UriParser.Parsers.EndPathBinder.DetermineParentNode(EndPathToken segmentToken)\r\n   at Microsoft.OData.Core.UriParser.Parsers.EndPathBinder.BindEndPath(EndPathToken endPathToken)\r\n   at Microsoft.OData.Core.UriParser.Parsers.MetadataBinder.BindEndPath(EndPathToken endPathToken)\r\n   at Microsoft.OData.Core.UriParser.Parsers.MetadataBinder.Bind(QueryToken token)\r\n   at Microsoft.OData.Core.UriParser.Parsers.BinaryOperatorBinder.GetOperandFromToken(BinaryOperatorKind operatorKind, QueryToken queryToken)\r\n   at Microsoft.OData.Core.UriParser.Parsers.BinaryOperatorBinder.BindBinaryOperator(BinaryOperatorToken binaryOperatorToken)\r\n   at Microsoft.OData.Core.UriParser.Parsers.MetadataBinder.BindBinaryOperator(BinaryOperatorToken binaryOperatorToken)\r\n   at Microsoft.OData.Core.UriParser.Parsers.MetadataBinder.Bind(QueryToken token)\r\n   at Microsoft.OData.Core.UriParser.Parsers.LambdaBinder.BindExpressionToken(QueryToken queryToken)\r\n   at Microsoft.OData.Core.UriParser.Parsers.LambdaBinder.BindLambdaToken(LambdaToken lambdaToken, BindingState state)\r\n   at Microsoft.OData.Core.UriParser.Parsers.MetadataBinder.BindAnyAll(LambdaToken lambdaToken)\r\n   at Microsoft.OData.Core.UriParser.Parsers.MetadataBinder.Bind(QueryToken token)\r\n   at Microsoft.OData.Core.UriParser.Parsers.LambdaBinder.BindExpressionToken(QueryToken queryToken)\r\n   at Microsoft.OData.Core.UriParser.Parsers.LambdaBinder.BindLambdaToken(LambdaToken lambdaToken, BindingState state)\r\n   at Microsoft.OData.Core.UriParser.Parsers.MetadataBinder.BindAnyAll(LambdaToken lambdaToken)\r\n   at Microsoft.OData.Core.UriParser.Parsers.MetadataBinder.Bind(QueryToken token)\r\n   at Microsoft.OData.Core.UriParser.Parsers.BinaryOperatorBinder.GetOperandFromToken(BinaryOperatorKind operatorKind, QueryToken queryToken)\r\n   at Microsoft.OData.Core.UriParser.Parsers.BinaryOperatorBinder.BindBinaryOperator(BinaryOperatorToken binaryOperatorToken)\r\n   at Microsoft.OData.Core.UriParser.Parsers.MetadataBinder.BindBinaryOperator(BinaryOperatorToken binaryOperatorToken)\r\n   at Microsoft.OData.Core.UriParser.Parsers.MetadataBinder.Bind(QueryToken token)\r\n   at Microsoft.OData.Core.UriParser.Parsers.FilterBinder.BindFilter(QueryToken filter)\r\n   at Microsoft.OData.Core.UriParser.ODataQueryOptionParser.ParseFilterImplementation(String filter, ODataUriParserConfiguration configuration, IEdmType elementType, IEdmNavigationSource navigationSource)\r\n   at Microsoft.OData.Core.UriParser.ODataQueryOptionParser.ParseFilter()\r\n   at System.Web.OData.Query.FilterQueryOption.get_FilterClause()\r\n   at System.Web.OData.Query.Validators.FilterQueryValidator.Validate(FilterQueryOption filterQueryOption, ODataValidationSettings settings)\r\n   at System.Web.OData.Query.FilterQueryOption.Validate(ODataValidationSettings validationSettings)\r\n   at System.Web.OData.Query.Validators.ODataQueryValidator.Validate(ODataQueryOptions options, ODataValidationSettings validationSettings)\r\n   at System.Web.OData.Query.ODataQueryOptions.Validate(ODataValidationSettings validationSettings)\r\n   at System.Web.OData.EnableQueryAttribute.ValidateQuery(HttpRequestMessage request, ODataQueryOptions queryOptions)\r\n   at System.Web.OData.EnableQueryAttribute.ExecuteQuery(Object response, HttpRequestMessage request, HttpActionDescriptor actionDescriptor)\r\n   at System.Web.OData.EnableQueryAttribute.OnActionExecuted(HttpActionExecutedContext actionExecutedContext)"
    }

我在某处读到了odata v4中尚未完全支持可空的datetime和datetimeoffset,这可能会导致此问题。反正是否有解决方法来实现上述查询?

2 个答案:

答案 0 :(得分:2)

我认为这是Simple.OData.Client的当前限制。它有一个内置的表达式解析器,其工作方式与其他LINQ提供程序类似。它将C#表达式转换为OData URI。会发生什么是它将HasValue视为OnHandLastUpdated的属性,即OnHandLastUpdated被解释为ComplexType,因此您收到此错误。

我将在Simple.OData.Client GitHub上注册一个问题,看看是否可以轻松修复。

我检查了库,看起来你可以像这样重写你的查询:

     var items =
oDataClient.For<ClientProductSku>()
    .Filter(x => x.ClientId == clientId && x.Product.SupplierProductSkuClient
        .All(y => y.SupplierProductSku.SupplierProductSkuOnHand
            .Any(z => z.OnHandLastUpdated.Value > DateTimeOffset.Now.AddMinutes(-5))))
    .Expand(UpdateSkuOnhandExpandTables)
    .FindEntriesAsync(annotations)
    .Result;
你能试试吗?我相信它应该有用。

答案 1 :(得分:0)

FWIW,我手动创建表达式树并遇到了同样的问题。我决定四处寻找并尝试一些东西,然后捎带@Vagif Abilov的答案将问题缩小到明确使用HasValue属性。

如果您null 进行比较而不是Nullable<DateTimeOffset>.HasValue,那么它可以正常工作,并且您不必捏造任何依赖可空测试或进行特殊测试的测试OData框架的案例。

因此,按照上面的示例,您应该可以:

 var items = oDataClient.For<ClientProductSku>()
    .Filter(x => x.ClientId == clientId && x.Product.SupplierProductSkuClient
    .All(y => y.SupplierProductSku.SupplierProductSkuOnHand
        .Any(z => z.OnHandLastUpdated != null && 
            z.OnHandLastUpdated.Value > DateTimeOffset.Now.AddMinutes(-5))))
    .Expand(UpdateSkuOnhandExpandTables)
    .FindEntriesAsync(annotations)
    .Result;