From acc428773f7296c1c5cbec8ef0cf888b0b1e6dec Mon Sep 17 00:00:00 2001 From: maxbruecken Date: Wed, 22 Dec 2021 17:48:59 +0100 Subject: [PATCH] Fixed filtering entities on nested collections with Any() or All() --- .../Expressions/ODataExpression.Format.cs | 16 ++++++++-------- .../Core/TypedExpressionsTests.cs | 10 ++++++++++ .../Entities/Order.cs | 2 ++ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/Simple.OData.Client.Core/Expressions/ODataExpression.Format.cs b/src/Simple.OData.Client.Core/Expressions/ODataExpression.Format.cs index d633a8aa..1080207c 100644 --- a/src/Simple.OData.Client.Core/Expressions/ODataExpression.Format.cs +++ b/src/Simple.OData.Client.Core/Expressions/ODataExpression.Format.cs @@ -194,16 +194,17 @@ private string FormatAnyAllFunction(ExpressionContext context) { var navigationPath = FormatCallerReference(); EntityCollection entityCollection; - if (!context.Session.Metadata.HasNavigationProperty(context.EntityCollection.Name, navigationPath)) + try { - //simple collection property - entityCollection = context.EntityCollection; + entityCollection = context.Session.Metadata.NavigateToCollection(context.EntityCollection, navigationPath); } - else + catch (UnresolvableObjectException) { - entityCollection = context.Session.Metadata.NavigateToCollection(context.EntityCollection, navigationPath); + //assume the simple collection property + entityCollection = null; } - + + var formattedNavigationPath = context.Session.Adapter.GetCommandFormatter().FormatNavigationPath(context.EntityCollection, navigationPath); string formattedArguments; if (!Function.Arguments.Any() && string.Equals(Function.FunctionName, ODataLiteral.Any, StringComparison.OrdinalIgnoreCase)) { @@ -215,8 +216,7 @@ private string FormatAnyAllFunction(ExpressionContext context) var expressionContext = new ExpressionContext(context.Session, entityCollection, targetQualifier, context.DynamicPropertiesContainerName); formattedArguments = $"{targetQualifier}:{FormatExpression(Function.Arguments.First(), expressionContext)}"; } - - var formattedNavigationPath = context.Session.Adapter.GetCommandFormatter().FormatNavigationPath(context.EntityCollection, navigationPath); + return FormatScope($"{formattedNavigationPath}/{Function.FunctionName.ToLower()}({formattedArguments})", context); } diff --git a/src/Simple.OData.Client.UnitTests/Core/TypedExpressionsTests.cs b/src/Simple.OData.Client.UnitTests/Core/TypedExpressionsTests.cs index b709ef91..f382efba 100644 --- a/src/Simple.OData.Client.UnitTests/Core/TypedExpressionsTests.cs +++ b/src/Simple.OData.Client.UnitTests/Core/TypedExpressionsTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; using System.Text.Json.Serialization; using Newtonsoft.Json; @@ -66,6 +67,15 @@ public void FilterEntitiesWithContainsAndNotContains() Expression> filter = x => ids.Contains(x.ProductID) && !names.Contains(x.ProductName); Assert.Equal("(ProductID in (1,2,3)) and not (ProductName in ('Chai','Milk','Water'))", ODataExpression.FromLinqExpression(filter).AsString(_session)); } + + [Fact] + public void FilterOrdersWithAnyInNestedCollection() + { + Expression> filter = x => x.Employee.Subordinates.Any(s => s.LastName == "Smith"); + var entityCollection = new EntityCollection("Orders"); + var expressionContext = new ExpressionContext(_session, entityCollection, null, null); + Assert.Equal("Employee/Subordinates/any(x1:x1/LastName eq 'Smith')", ODataExpression.FromLinqExpression(filter).Format(expressionContext)); + } } public abstract class TypedExpressionTests : CoreTestBase diff --git a/src/Simple.OData.Client.UnitTests/Entities/Order.cs b/src/Simple.OData.Client.UnitTests/Entities/Order.cs index b02acd30..19fdf45e 100644 --- a/src/Simple.OData.Client.UnitTests/Entities/Order.cs +++ b/src/Simple.OData.Client.UnitTests/Entities/Order.cs @@ -11,4 +11,6 @@ public class Order public DateTimeOffset ShippedDateTimeOffset { get; set; } public OrderDetail[] OrderDetails { get; set; } + + public Employee Employee { get; set; } }