Проблемы при запросе связанных сущностей со свойствами, которые могут иметь значение datetimetimeoffset
Я использую 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)"
}
Я где-то читал, что обнуляемые datetime и datetimeoffset еще не полностью поддерживаются в odata v4, что может вызвать эту проблему. Есть ли способ обойти вышеуказанный запрос?
2 ответа
Я считаю, что это текущее ограничение Simple.OData.Client. Он имеет встроенный анализатор выражений, который работает аналогично другим поставщикам LINQ. Он преобразует выражение C# в URI OData. Что происходит, так это то, что он обрабатывает 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;
Вы можете попробовать это? Я считаю, что это должно работать.
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;