diff --git a/QueryKit.IntegrationTests/Tests/DatabaseFilteringTests.cs b/QueryKit.IntegrationTests/Tests/DatabaseFilteringTests.cs index ce2c908..0bad1e1 100644 --- a/QueryKit.IntegrationTests/Tests/DatabaseFilteringTests.cs +++ b/QueryKit.IntegrationTests/Tests/DatabaseFilteringTests.cs @@ -366,6 +366,27 @@ public async Task can_filter_within_collection_long() recipes[0].Id.Should().Be(fakeRecipeOne.Id); } + [Fact] + public async Task can_filter_by_guid_for_collection() + { + var testingServiceScope = new TestingServiceScope(); + var fakeRecipeOne = new FakeRecipeBuilder().Build(); + var ingredient = new FakeIngredientBuilder() + .Build(); + + fakeRecipeOne.AddIngredient(ingredient); + + await testingServiceScope.InsertAsync(fakeRecipeOne); + + var input = $""" Ingredients.Id == "{ingredient.Id}" """; + var queryableRecipes = testingServiceScope.DbContext().Recipes; + var appliedQueryable = queryableRecipes.ApplyQueryKitFilter(input); + var recipes = await appliedQueryable.ToListAsync(); + + recipes.Count.Should().Be(1); + recipes[0].Ingredients.First().Id.Should().Be(ingredient.Id); + } + [Fact(Skip = "Can not handle nested collections yet.")] public async Task can_filter_by_string_for_nested_collection() { diff --git a/QueryKit/FilterParser.cs b/QueryKit/FilterParser.cs index c91f259..f7c3fb7 100644 --- a/QueryKit/FilterParser.cs +++ b/QueryKit/FilterParser.cs @@ -430,20 +430,11 @@ private static Parser ComparisonExprParser(ParameterExpression pa if (temp.leftExpr.Type == typeof(Guid) || temp.leftExpr.Type == typeof(Guid?)) { - var toStringMethod = typeof(Guid).GetMethod("ToString", Type.EmptyTypes); - - Expression leftExpr = temp.leftExpr.Type == typeof(Guid?) ? - Expression.Condition( - Expression.Property(temp.leftExpr, "HasValue"), - Expression.Call(Expression.Property(temp.leftExpr, "Value"), toStringMethod!), - Expression.Constant(null, typeof(string)) - ) : - Expression.Call(temp.leftExpr, toStringMethod!); - - return temp.op.GetExpression(leftExpr, CreateRightExpr(temp.leftExpr, temp.right, temp.op), config?.DbContextType); + var guidStringExpr = HandleGuidConversion(temp.leftExpr, temp.leftExpr.Type); + return temp.op.GetExpression(guidStringExpr, CreateRightExpr(temp.leftExpr, temp.right, temp.op), + config?.DbContextType); } - var rightExpr = CreateRightExpr(temp.leftExpr, temp.right, temp.op); return temp.op.GetExpression(temp.leftExpr, rightExpr, config?.DbContextType); }); @@ -495,8 +486,9 @@ private static Parser ComparisonExprParser(ParameterExpression pa var propertyInfoForMethod = GetPropertyInfo(genericArgType, propName); var lambdaBody = Expression.PropertyOrField(innerParameter, propertyInfoForMethod.Name); var selectLambda = Expression.Lambda(lambdaBody, innerParameter); + var selectResult = Expression.Call(null, selectMethod, member, selectLambda); - return Expression.Call(null, selectMethod, member, selectLambda); + return HandleGuidConversion(selectResult, propertyType, "Select"); } } } @@ -582,6 +574,35 @@ private static Parser OrExprParser(ParameterExpression parameter, AndExprParser(parameter, config), (op, left, right) => op.GetExpression(left, right) ); + + private static Expression GetGuidToStringExpression(Expression leftExpr) + { + var toStringMethod = typeof(Guid).GetMethod("ToString", Type.EmptyTypes); + + return leftExpr.Type == typeof(Guid?) ? + Expression.Condition( + Expression.Property(leftExpr, "HasValue"), + Expression.Call(Expression.Property(leftExpr, "Value"), toStringMethod!), + Expression.Constant(null, typeof(string)) + ) : + Expression.Call(leftExpr, toStringMethod!); + } + + private static Expression HandleGuidConversion(Expression expression, Type propertyType, string? selectMethodName = null) + { + if (propertyType != typeof(Guid) && propertyType != typeof(Guid?)) return expression; + + if (string.IsNullOrWhiteSpace(selectMethodName)) return GetGuidToStringExpression(expression); + + var selectMethod = typeof(Enumerable).GetMethods() + .First(m => m.Name == selectMethodName && m.GetParameters().Length == 2) + .MakeGenericMethod(propertyType, typeof(string)); + + var param = Expression.Parameter(propertyType, "g"); + var toStringLambda = Expression.Lambda(GetGuidToStringExpression(param), param); + + return Expression.Call(selectMethod, expression, toStringLambda); + } }