QueryableExtension.ToFilterString blank on only IEnumerable

When using RadzenDataFilter with a IEnumerable<int> filter value, .ToFilterString() and ToODataString() both return a blank string.

As soon as a regular string-based filter is added, it generates the values.

The below code should reproduce the issue.

@page "/dataFilterBug"

<RadzenButton Text="IEnumerable Only Filter" Click="EnumerableFilter" />
<RadzenButton Text="Text Only Filter" Click="TextFilter" />
<RadzenButton Text="Mixed Filter" Click="MixedFilter" />
<RadzenDataFilter @ref="dataFilter" TItem="Demo" ViewChanged="@(()=>StateHasChanged())" >
    <Properties>
        <RadzenDataFilterProperty Type="@typeof(string)" Property="@nameof(Demo.Title)" />
        <RadzenDataFilterProperty 
        Type="@typeof(IEnumerable<int>)" 
        Property="@nameof(Demo.DemoValue)"
        FilterOperator="FilterOperator.Contains"
        FilterOperators="@(new [] { FilterOperator.Contains, FilterOperator.DoesNotContain})">
            <FilterTemplate Context="field">
                <RadzenDropDown @bind-Value="@(field.FilterValue)"
                Style="width:100%;"
                Data="@(new List<int> { 1, 2, 3, 4, 5 })"
                AllowClear="true"
                Multiple="true" />
            </FilterTemplate>
        </RadzenDataFilterProperty>
    </Properties>
</RadzenDataFilter>

@if (dataFilter != null)
{
    <p>Filter String: @dataFilter.ToFilterString()</p>
    <p>OData String: @dataFilter.ToODataFilterString()</p>
}

@code {



    RadzenDataFilter<Demo> dataFilter;




    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await base.OnAfterRenderAsync(firstRender);

        if (firstRender)
        {
            await EnumerableFilter();
        }
    }

    async Task EnumerableFilter()
    {
        await dataFilter.ClearFilters();
        await dataFilter.AddFilter(new CompositeFilterDescriptor()
            {
                Property = nameof(Demo.DemoValue),
                FilterValue = new int[] { 2 },
                FilterOperator = FilterOperator.Contains
            });
        StateHasChanged();
    }

    async Task TextFilter()
    {
        await dataFilter.ClearFilters();
        await dataFilter.AddFilter(new CompositeFilterDescriptor()
            {
                Property = nameof(Demo.Title),
                FilterValue = "t",
                FilterOperator = FilterOperator.Contains
            });
        StateHasChanged();
    }

    async Task MixedFilter()
    {
        await dataFilter.ClearFilters();
        await dataFilter.AddFilter(new CompositeFilterDescriptor()
            {
                Property = nameof(Demo.DemoValue),
                FilterValue = new int[] { 2 },
                FilterOperator = FilterOperator.Contains
            });
        await dataFilter.AddFilter(new CompositeFilterDescriptor()
            {
                Property = nameof(Demo.Title),
                FilterValue = "t",
                FilterOperator = FilterOperator.Contains
            });

        StateHasChanged();
    }

    public class Demo
    {
        public string Title { get; set; }
        public int DemoValue { get; set; }
    }

    


}

Thanks for the report! It will be fixed immediately!

Maybe the same origin: LoadDataArgs.Filter is an empty string with current version even if LoadDataArgs.Filters contains an integer filter. (Was not in version 8.4.2)
You can check your own example Blazor DataGrid Component - OData Service | Free UI Components by Radzen when enabling the filter on the EmployeeID column

The fix is already released in latest Radzen.Blazor (8.6.2).

Thanks for that. It’s fixed it in my test case case - I’m not getting a blank string.

However, the way I’m calling it my application is now getting me a new exception.

System.InvalidOperationException: No generic method 'Contains' on type 'System.Linq.Enumerable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic. 
   at System.Linq.Expressions.Expression.FindMethod(Type type, String methodName, Type[] typeArgs, Expression[] args, BindingFlags flags)
   at Radzen.QueryableExtension.GetExpression[T](ParameterExpression parameter, FilterDescriptor filter, FilterCaseSensitivity filterCaseSensitivity, Type type)
   at Radzen.QueryableExtension.AddWhereExpression[T](ParameterExpression parameter, CompositeFilterDescriptor filter, List`1& filterExpressions, FilterCaseSensitivity filterCaseSensitivity)
   at Radzen.QueryableExtension.ToFilterString[T](IEnumerable`1 filters, LogicalFilterOperator logicalFilterOperator, FilterCaseSensitivity filterCaseSensitivity)

I’ll try and make a reproducible case now, as it maybe an issue with serializing out RadzenDataFilter.Filters

Okay I think it’s just how I’m serilizing/deserilizing the filters. I redid it with the built-in System.Text.Json rather than legacy Newtonsoft and get a different exception.

@page "/dataFilterBug"
@using System.Text.Json
@using System.Text.Json.Serialization

<RadzenButton Text="IEnumerable Only Filter" Click="EnumerableFilter" />
<RadzenButton Text="Text Only Filter" Click="TextFilter" />
<RadzenButton Text="Mixed Filter" Click="MixedFilter" />
<RadzenButton Text="Serilization Round Trip" Click="Serilize" />
<RadzenDataFilter @ref="dataFilter" TItem="Demo" ViewChanged="@(()=>StateHasChanged())" >
    <Properties>
        <RadzenDataFilterProperty Type="@typeof(string)" Property="@nameof(Demo.Title)" />
        <RadzenDataFilterProperty 
        Type="@typeof(IEnumerable<int>)"
        Property="@nameof(Demo.DemoValue)"
        FilterOperator="FilterOperator.Contains"
        FilterOperators="@(new [] { FilterOperator.Contains, FilterOperator.DoesNotContain})">
            <FilterTemplate Context="field">
                <RadzenDropDown @bind-Value="@(field.FilterValue)"
                Style="width:100%;"
                Data="@(new List<int> { 1, 2, 3, 4, 5 })"
                AllowClear="true"
                Multiple="true" />
            </FilterTemplate>
        </RadzenDataFilterProperty>
    </Properties>
</RadzenDataFilter>

@if (dataFilter != null)
{
    <p>Filter String: @dataFilter.ToFilterString()</p>
    <p>OData String: @dataFilter.ToODataFilterString()</p>
}

@code {

    public class JsonTypeConverter : JsonConverter<Type>
    {
        public override Type? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            string? typeName = reader.GetString();
            return typeName == null ? null : Type.GetType(typeName);
        }

        public override void Write(Utf8JsonWriter writer, Type value, JsonSerializerOptions options)
        {
            // Use AssemblyQualifiedName to ensure the type can be reloaded later
            writer.WriteStringValue(value.AssemblyQualifiedName);
        }
    }

    RadzenDataFilter<Demo> dataFilter;

    async Task Serilize()
    {
        var options = new JsonSerializerOptions
        {
            Converters = { new JsonTypeConverter() },
            WriteIndented = true
        };

        await dataFilter.ClearFilters();
        await dataFilter.AddFilter(new CompositeFilterDescriptor()
            {
                Property = nameof(Demo.DemoValue),
                FilterValue = new int[] { 2 },
                FilterOperator = FilterOperator.Contains
            });
        // Serilize the filters out 
        var json = JsonSerializer.Serialize(dataFilter.Filters, options);

        // and bring them back in.
        var filters = JsonSerializer.Deserialize<IEnumerable<CompositeFilterDescriptor>>(json, options);
        dataFilter.ClearFilters();
        dataFilter.Filters = filters;
        StateHasChanged();
    }


    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await base.OnAfterRenderAsync(firstRender);

        if (firstRender)
        {
            await EnumerableFilter();
        }
    }

    async Task EnumerableFilter()
    {
        await dataFilter.ClearFilters();
        await dataFilter.AddFilter(new CompositeFilterDescriptor()
            {
                Property = nameof(Demo.DemoValue),
                FilterValue = new int[] { 2 },
                FilterOperator = FilterOperator.Contains
            });
        StateHasChanged();
    }

    async Task TextFilter()
    {
        await dataFilter.ClearFilters();
        await dataFilter.AddFilter(new CompositeFilterDescriptor()
            {
                Property = nameof(Demo.Title),
                FilterValue = "t",
                FilterOperator = FilterOperator.Contains
            });
        StateHasChanged();
    }

    async Task MixedFilter()
    {
        await dataFilter.ClearFilters();
        await dataFilter.AddFilter(new CompositeFilterDescriptor()
            {
                Property = nameof(Demo.DemoValue),
                FilterValue = new int[] { 2 },
                FilterOperator = FilterOperator.Contains
            });
        await dataFilter.AddFilter(new CompositeFilterDescriptor()
            {
                Property = nameof(Demo.Title),
                FilterValue = "t",
                FilterOperator = FilterOperator.Contains
            });

        StateHasChanged();
    }

    public class Demo
    {
        public string Title { get; set; }
        public int DemoValue { get; set; }
    }

    


}

System.ArgumentException
  HResult=0x80070057
  Message=Argument types do not match
  Source=System.Linq.Expressions
  StackTrace:
   at System.Linq.Expressions.Expression.Constant(Object value, Type type)
   at Radzen.QueryableExtension.GetExpression[T](ParameterExpression parameter, FilterDescriptor filter, FilterCaseSensitivity filterCaseSensitivity, Type type)
   at Radzen.QueryableExtension.AddWhereExpression[T](ParameterExpression parameter, CompositeFilterDescriptor filter, List`1& filterExpressions, FilterCaseSensitivity filterCaseSensitivity)
   at Radzen.QueryableExtension.ToFilterString[T](RadzenDataFilter`1 dataFilter)
   at Demo.DataFilterBug.BuildRenderTree(RenderTreeBuilder __builder) in 

So will look to do the serialising separately.

Not sure how this is related to our extension methods - you cannot serialize/deserialize arrays using default/simple converters.This is what you will get for FilterValue which is definetely not something supported by our filtering:

Check how DataGridSettings are serialized/deserialized for reference.

Sorry, it’s not the extension methods - they're fine! Just how I'm doing the serialisation :slight_smile: