pdevito3 / QueryKit

🎛️ QueryKit is a .NET library that makes it easier to query your data by providing a fluent and intuitive syntax for filtering and sorting.
Apache License 2.0
147 stars 13 forks source link

Support for comparing numeric collection types other than int #45

Closed ChasakisD closed 2 months ago

ChasakisD commented 2 months ago

Hi! First of all, I would like to say a thank you for this awesome library and effort you have put into it.

I have a case where in your test cases, let's say Ingredient class has a new property

public long MinimumQuantity { get; private set; }

Then the following test should succeed

[Fact]
public void can_filter_within_collection_long()
{
    var minQuantityLong = 2L;
    var fakeRecipeOne = new FakeRecipeBuilder().Build();
    fakeRecipeOne.AddIngredient(Ingredient.Create(new IngredientForCreation(){MinimumQuantity =  minQuantityLong}));
    var input = $"Ingredients.MinimumQuantity == {minQuantityLong}";
    var config = new QueryKitConfiguration(settings =>
    {
        settings.Property<Recipe>(x => x.Ingredients.Select(y => y.MinimumQuantity)).PreventSort();
    });
    var filterExpression = FilterParser.ParseFilter<Recipe>(input, config);

    filterExpression.Compile().Invoke(fakeRecipeOne).Should().BeTrue();
}

But it fails with the following exception

The binary operator Equal is not defined for the types 'System.Int64' and 'System.Int32'.

From my understanding in this line, you try to parse int, but you ignore type compatibility. https://github.com/pdevito3/QueryKit/blob/245ad0d8822d68a68e42ce0cdbe112347c683dfd/QueryKit/FilterParser.cs#L177-L177

A check of the generic type param of the enumerable should be made in order to convert the according constant values to the equivalent type (in our case long instead of int)

I tried the following code to replace that if statement but I have some failing tests

if (targetType.GetGenericArguments().All(arg => arg != typeof(string)))
{
    var propertyType = targetType.GetGenericArguments()[0];
    var converter = TypeDescriptor.GetConverter(propertyType);
    if (converter.CanConvertFrom(typeof(string)))
    {
        var propertyValue = converter.ConvertFromInvariantString(right);
        return Expression.Constant(propertyValue, propertyType);
    }
}

I would appreciate your help in here!

Thanks again!

pdevito3 commented 2 months ago

👋 thanks for the effort and callout here! Yeah this should be doable but not sure exactly what the issue is without digging into it a bit. This looks like the right area are first glance though. Might just need some finer tweaks to make the rest of the flow happy.