CheckBoxList filter, Sub Property, and LoadData

After adding a LoadData method on the CheckBoxFilter demo, filtering on the Product Name column fails.

With the code below, filter on the Product Name column, it errors on query.Where(args.Filter)

Blazor DataGrid Component - Excel like filtering | Free UI Components by Radzen

@using RadzenBlazorDemos.Data
@using RadzenBlazorDemos.Models.Northwind
@using Microsoft.EntityFrameworkCore
@using System.Text.Json
@using System.Linq.Dynamic.Core;
@using System.Text.Json.Serialization

@inherits DbContextPage

<style>
    .rz-grid-table {
    width: unset;
    }
</style>

<RadzenDataGrid @ref="grid" AllowFiltering="true" AllowColumnResize="true" 
FilterMode="FilterMode.CheckBoxList" PageSize="5" AllowPaging="true" AllowSorting="true" Data="@orders"
AllowColumnPicking="true"
LoadData="GridLoadData"
>
    <Columns>
        <RadzenDataGridColumn Property="OrderID" Title="Order ID" />
        <RadzenDataGridColumn @ref=ProductNameColumn Property="@nameof(Order.OrderDetails)" FilterProperty="Product.ProductName" Title="Product Name"
                              Type="typeof(IEnumerable<OrderDetail>)" Sortable="false" FilterOperator="FilterOperator.In">
            <Template>
                @(string.Join(", ", context.OrderDetails.Select(od => od.Product.ProductName)))
            </Template>
        </RadzenDataGridColumn>
        <RadzenDataGridColumn Property="Customer.CompanyName" Title="Customer" AllowCheckBoxListVirtualization="false" />
        <RadzenDataGridColumn Property="ProductDiscontinued" Title="Discontinued" FormatString="{0}" FormatProvider="@myBooleanProvider" />
        <RadzenDataGridColumn Property="Employee.LastName" Title="Employee">
            <Template Context="order">
                <RadzenImage Path="@order.Employee?.Photo" Style="width: 32px; height: 32px;" class="rz-border-radius-4 rz-me-2" AlternateText="@(order.Employee?.FirstName + " " + order.Employee?.LastName)" />
                @order.Employee?.FirstName @order.Employee?.LastName
            </Template>
        </RadzenDataGridColumn>

        <RadzenDataGridColumn Property="@nameof(Order.OrderDate)" Title="Order Date" FormatString="{0:d}" />
        <RadzenDataGridColumn Property="@nameof(Order.RequiredDate)" Title="Required Date" FormatString="{0:d}" />
        <RadzenDataGridColumn Property="@nameof(Order.ShippedDate)" Title="Shipped Date" FormatString="{0:d}" />
        <RadzenDataGridColumn Property="@nameof(Order.ShipName)" Title="Ship Name" />
        <RadzenDataGridColumn Property="@nameof(Order.ShipCountry)" Title="Ship Country" />
    </Columns>
</RadzenDataGrid>

@code {
    IEnumerable<Order> allOrders;
    List<Order> orders;
    RadzenDataGrid<Order> grid;
    MyBooleanProvider myBooleanProvider = new MyBooleanProvider();
    RadzenDataGridColumn<Order> ProductNameColumn { get; set; }

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();

        allOrders = orders = await Task.FromResult(dbContext.Orders.Include("OrderDetails.Product").Include("Customer").Include("Employee").ToList().Select(o =>
            {
                o.ProductDiscontinued = o.OrderDetails.FirstOrDefault()?.Product?.Discontinued;
                return o;
            }).ToList());
    }

    public class MyBooleanProvider : IFormatProvider, ICustomFormatter
    {
        public string Format(string format, object arg, IFormatProvider formatProvider)
        {
            return object.Equals(arg, true) ? "Yes" : object.Equals(arg, false) ? "No" : "No value";
        }

        public object GetFormat(Type formatType)
        {
            if (formatType == typeof(ICustomFormatter))
            {
                return this;
            }

            return null;
        }
    }

    private void GridLoadData(LoadDataArgs args)
    {
        var options = new JsonSerializerOptions() { WriteIndented = true };
        options.Converters.Add(new JsonStringEnumConverter() );
        Console.WriteLine(JsonSerializer.Serialize(args,options));

        var query = allOrders.ToList().AsQueryable();

        if (!String.IsNullOrEmpty(args.Filter))
        {
            query = query.Where(args.Filter);
        }

        if (!String.IsNullOrEmpty(args.OrderBy))
        {
            query = query.OrderBy(args.OrderBy);
        }

        if (grid.AllowPaging && args.Skip.HasValue && args.Top.HasValue)
            query = query.Skip(args.Skip.Value).Take(args.Top.Value);

        orders.Clear();
        orders.AddRange(query.ToList());
    }
}

I was able to replicate the problem - we will do our best to provide fix later the week.

Found two errors..
On the master branch, using CheckBoxList and LoadData, I'm getting a similar error for enums as the one above. Blazor DataGrid Component - Enum Filtering | Free UI Components by Radzen

Using the code below: Open Nullable Status, select a value, and apply.
Error happens on Gender column too, but my property is a nullable enum.

When filtering on nullable status, the filter passed into LoadData is
Status in new Nullable\u00601[ ]{Active}`,
vs
Gender in new GenderType[ ]{Mr}

The second happens when loading the page without setting employees. Happens when employees is null or an empty list.

Comment out the line near the bottom. Then open a filter

      Unhandled exception rendering component: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
   at System.Collections.Generic.List`1[[System.Object, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].get_Item(Int32 index)
   at Radzen.Blazor.RadzenDataGridHeaderCell`1.<LoadFilterValues>d__6[[RadzenBlazorDemos.Pages.DataGridColumnEnumFilter.Employee, RadzenBlazorDemos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext() in C:\radzen-blazor\Radzen.Blazor\RadzenDataGridHeaderCell.razor:line 297
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
   at Radzen.Blazor.RadzenListBox`1.<OnAfterRenderAsync>d__19[[System.Collections.Generic.IEnumerable`1[[System.Object, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext() in C:\radzen-blazor\Radzen.Blazor\RadzenListBox.razor.cs:line 134
crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
   at System.Collections.Generic.List`1[[System.Object, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].get_Item(Int32 index)
   at Radzen.Blazor.RadzenDataGridHeaderCell`1.<LoadFilterValues>d__6[[RadzenBlazorDemos.Pages.DataGridColumnEnumFilter.Employee, RadzenBlazorDemos, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext() in C:\radzen-blazor\Radzen.Blazor\RadzenDataGridHeaderCell.razor:line 297
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
   at Radzen.DropDownBase`1.<LoadItems>d__8[[System.Collections.Generic.IEnumerable`1[[System.Object, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext() in C:\radzen-blazor\Radzen.Blazor\DropDownBase.cs:line 55
   at Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize`1[[System.Object, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].BuildRenderTree(RenderTreeBuilder builder)
   at Microsoft.AspNetCore.Components.ComponentBase.<.ctor>b__7_0(RenderTreeBuilder builder)
   at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment, Exception& renderFragmentException)

Code:

@using System.Linq.Dynamic.Core
@using System.ComponentModel.DataAnnotations
@using System.Text.Json
@using System.Text.Json.Serialization

<RadzenDataGrid @ref=grid Data=@employees AllowFiltering="true" AllowPaging="true" AllowSorting="true" ColumnWidth="200px" 
Count="count"
FilterMode="FilterMode.CheckBoxList"
LoadData="LoadData">
    <Columns>
        <RadzenDataGridColumn Property="@nameof(Employee.ID)" Title="ID" />
        <RadzenDataGridColumn Property="@nameof(Employee.Gender)" Title="Gender" />
        <RadzenDataGridColumn Property="@nameof(Employee.Status)" Title="Nullable Status" />
        <RadzenDataGridColumn Property="@nameof(Employee.Color)" Title="Favorite Color (Display Attribute in Filter)" />
    </Columns>
</RadzenDataGrid>

@code {
    List<Employee> allEmployees = new();
    List<Employee> employees = new();
    int count { get; set; }

    RadzenDataGrid<Employee> grid;

    private void LoadData(LoadDataArgs args)
    {
        var options = new JsonSerializerOptions() { WriteIndented = true };
        options.Converters.Add(new JsonStringEnumConverter());
        Console.WriteLine(JsonSerializer.Serialize(args, options));

        var query = allEmployees.ToList().AsQueryable();

        if (!String.IsNullOrEmpty(args.Filter))
        {
            query = query.Where(args.Filter);
        }

        if (!String.IsNullOrEmpty(args.OrderBy))
        {
            query = query.OrderBy(args.OrderBy);
        }

        if (grid.AllowPaging && args.Skip.HasValue && args.Top.HasValue)
            query = query.Skip(args.Skip.Value).Take(args.Top.Value);

        employees.Clear();
        employees.AddRange(query.ToList());


    }

    public class Employee
    {
        public int ID { get; set; }
        public GenderType Gender { get; set; }
        public StatusType? Status { get; set; }
        public ColorType Color { get; set; }
    }

    public enum GenderType
    {
        Ms,
        Mr,
        Unknown,
    }

    public enum ColorType
    {
        Red,
        Green,
        Blue,
        [Display(Description = "Almond Green")]
        AlmondGreen,
        [Display(Description = "Amber Gray")]
        AmberGray,
        [Display(Description = "Apple Blue... ")]
        AppleBlueSeaGreen,
        //[Display(Description = "Miss", ResourceType = typeof(ResourceFile)] localization example
        [Display(Description = "Azure")]
        AzureBlue,

    }

    public enum StatusType
    {
        Inactive,
        Active,
    }

    protected override void OnInitialized()
    {
        allEmployees = Enumerable.Range(0, 10).Select(i =>
            new Employee
                {
                    ID = i,
                    Gender = i < 3 ? GenderType.Mr : i < 6 ? GenderType.Ms : GenderType.Unknown,
                    Status = i < 3 ? StatusType.Active : i < 6 ? StatusType.Inactive : null,
                    Color = i < 2 ? ColorType.Red : i < 4 ? ColorType.AlmondGreen : i < 6 ? ColorType.AppleBlueSeaGreen : ColorType.AzureBlue,
                })
            .ToList();

//COMMENT THIS LINE
        employees = allEmployees.ToList();
    }
}

On a different note, have you thought about setting up prereleases for the nuget package so minor changes between releases can get a nuget package?

You can use something like GitVersion to autoincrement each commit to master as a prerelease version, then tag master with a version and rebuild it to cut a full release. It has a command line interface that will update the csproj w/ the version it detects.

No. We provide updates quite often (twice a week at least) and I don’t think we need prereleases for minor fixes.

The last release fixed the Gender column, but the Nullable Status still has errors.

I appreciate frequent updates. I was thinking it would be helpful to let users test fixes while waiting for it to be released to the community at large.

Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: 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. 
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 System.Linq.Expressions.Expression.Call(Type type, String methodName, Type[] typeArguments, Expression[] arguments)
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseIn()
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseAndOperator()
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseOrOperator()
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseLambdaOperator()
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseNullCoalescingOperator()
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseConditionalOperator()
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.Parse(Type resultType, Boolean createParameterCtor)
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(Type delegateType, ParsingConfig parsingConfig, Boolean createParameterCtor, ParameterExpression[] parameters, Type resultType, String expression, Object[] values)
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(ParsingConfig parsingConfig, Boolean createParameterCtor, ParameterExpression[] parameters, Type resultType, String expression, Object[] values)
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(ParsingConfig parsingConfig, Boolean createParameterCtor, Type itType, Type resultType, String expression, Object[] values)
   at System.Linq.Dynamic.Core.DynamicQueryableExtensions.Where(IQueryable source, ParsingConfig config, String predicate, Object[] args)
   at System.Linq.Dynamic.Core.DynamicQueryableExtensions.Where[Employee](IQueryable`1 source, ParsingConfig config, String predicate, Object[] args)
   at System.Linq.Dynamic.Core.DynamicQueryableExtensions.Where[Employee](IQueryable`1 source, String predicate, Object[] args)
   at RadzenBlazorDemos.DynamicComponent.LoadData(LoadDataArgs args)
   at System.Object.InvokeStub_DynamicComponent.LoadData(Object , Span`1 )
   at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
   at Radzen.Blazor.RadzenDataGrid`1.<InvokeLoadData>d__521[[RadzenBlazorDemos.DynamicComponent.Employee, RadzenBlazorDemos.DynamicAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext()
   at Radzen.Blazor.RadzenDataGrid`1.<ReloadInternal>d__519[[RadzenBlazorDemos.DynamicComponent.Employee, RadzenBlazorDemos.DynamicAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext()
   at Radzen.Blazor.RadzenDataGrid`1.<OnFilter>d__115[[RadzenBlazorDemos.DynamicComponent.Employee, RadzenBlazorDemos.DynamicAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext()
   at Radzen.Blazor.RadzenDataGrid`1.<ApplyFilter>d__136[[RadzenBlazorDemos.DynamicComponent.Employee, RadzenBlazorDemos.DynamicAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext()
   at Radzen.Blazor.RadzenDataGridHeaderCell`1.<ApplyFilter>d__13[[RadzenBlazorDemos.DynamicComponent.Employee, RadzenBlazorDemos.DynamicAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext()
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)

We still don't have fix for this, we will publish update when we are ready, if you know how to fix this feel free to submit pull request.

One can always use the master branch to test the latest state.

As a workaround you can use our extension method passing the DataGrid columns instead the default Dynamic LINQ Where():

...
if (!String.IsNullOrEmpty(args.Filter))
{
    query = query.Where(grid.ColumnsCollection);
}
...

This errors when filtering on the null value.

I was able to get filtering to work by casting the column and the values to int?

args.Filter = "int?(Status) in new []{int?(null)}";
args.Filter = "int?(Status) in new []{int?(0)}";
args.Filter = "int?(Status) in new []{int?(null), int?(1)}";