RadzenDataGrid Filter in LoadDataArgs is Null or Empty

Hi i'm on the way to encapsulate RadzenDataGrid to be used with generic entity. It actually works to populate the grid with dynamic columns creation and upadting rows using callback. but i failed to make builtin filters work. the LoadDataArgs args.Filter is always empty ... To clarified i use ExpandObject as datastructure for my data, perhaps it may hurt the filtering logic, but for me it not explained why args are not correclty sets when call to loadData fired. Somebody could clarify this point for me ?
Regards
excerpt from my implementation :
In razor page

@inject ILogger<DynamicPageRadzen> Log
@inject GenericDataService<Programmation> DataService

<h3>generic entity with RadzenDataGrid Dynamic</h3>

<DynamicRadzenDataGrid Items="pageData" ItemsCount="totalRecords" ItemsPerPage="@itemsPerPage" OnSaveItem="HandleSaveAsync" OnLoadData="LoadData" />

@code {
    private List<ExpandoObject>? pageData;
    private int totalRecords;
    private string pageName = "test-dynamic-radzen";
    private int itemsPerPage = 10;

    protected override async Task OnInitializedAsync()
    {
        var data = await DataService.GetTopAsync(itemsPerPage);
        pageData = data.Select(I => ExpandoHelper.ToExpando(I)).ToList();
        totalRecords = await DataService.GetCountAsync();
    }

    private async Task<List<ExpandoObject>?> LoadData(LoadDataArgs args)
    {
        try
        {
            var query = DataService.Query();
            if (!string.IsNullOrWhiteSpace(args.Filter))
            {
                query = query.Where(args.Filter); // filter using System.Linq.Dynamic.Core
            }
            else
            {
                Log.LogInformation($"In {pageName} LoadData, args.Filter is null or Empty");
            }

In my DynamicRadzenDatagrid component

@using System.Dynamic
@using Radzen
@using Radzen.Blazor

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

@if (Items != null && Items.Any())
{
    var firstItem = (IDictionary<string, object?>)Items.First();

    <RadzenDataGrid @ref="grid"
    Data="@Items"
    TItem="ExpandoObject"
    EditMode="DataGridEditMode.Single"
    AllowFiltering="true"
    AllowSorting="true"
    AllowPaging="true"
    FilterMode="FilterMode.Advanced"
    PageSize="@ItemsPerPage"
    Count="@ItemsCount"
    LoadData="@LoadData"
    RowUpdate="@OnUpdateRow"
    GridLines="@GridLines" 
    Density="@Density"

    ColumnWidth="200px">
        <Columns>
            @foreach (var key in firstItem.Keys)
            {
                <RadzenDataGridColumn TItem="ExpandoObject" Property="@key" Title="@key">
                    <Template Context="data">
                        @{
                            var dict = (IDictionary<string, object?>)data;
                            var value = dict[key];
                            @value
                        }
                    </Template>
                    <EditTemplate Context="data">
                        @{
                            var dict = (IDictionary<string, object?>)data;
                            var currentValue = dict[key];
                            var valueType = currentValue?.GetType();

                            if (valueType == typeof(bool))
                            {
                                bool val = currentValue as bool? ?? false;
                                <RadzenCheckBox TValue="bool"
                                Value="@val"
                                ValueChanged="@(v => dict[key] = v)" />
                            }
                            else if (valueType == typeof(DateTime))
                            {
                                DateTime val = currentValue as DateTime? ?? DateTime.MinValue;
                                <RadzenDatePicker TValue="DateTime"
                                Value="@val"
                                ValueChanged="@(v => dict[key] = v)"
                                Style="width:100%" />
                            }
                            else if (valueType == typeof(int))
                            {
                                int val = currentValue as int? ?? 0;
                                <RadzenNumeric TValue="int"
                                Value="@val"
                                ValueChanged="@(v => dict[key] = v)"
                                Style="width:100%" />
                            }
                            else if (valueType == typeof(double))
                            {
                                double val = currentValue as double? ?? 0;
                                <RadzenNumeric TValue="double"
                                Value="@val"
                                ValueChanged="@(v => dict[key] = v)"
                                Style="width:100%" />
                            }
                            else
                            {
                                string val = currentValue?.ToString() ?? string.Empty;
                                <RadzenTextBox Value="@val"
                                ValueChanged="@(v => dict[key] = v)"
                                Style="width:100%" />
                            }
                        }
                    </EditTemplate>
                </RadzenDataGridColumn>
            }
            
            <RadzenDataGridColumn TItem="ExpandoObject" Filterable="false" Sortable="false" TextAlign="TextAlign.Right" Width="150px" Frozen="true" FrozenPosition="FrozenColumnPosition.Right">      
                <Template Context="data">
                    <RadzenButton Icon="edit"
                    ButtonStyle="ButtonStyle.Primary"
                    Size="ButtonSize.Small"
                    Click="@(() => grid.EditRow(data))" />
                </Template>
                <EditTemplate Context="data">
                    <RadzenButton Icon="save"
                    ButtonStyle="ButtonStyle.Success"
                    Size="ButtonSize.Small"
                    Click="@(() => grid.UpdateRow(data))" />
                    <RadzenButton Icon="cancel"
                    ButtonStyle="ButtonStyle.Light"
                    Size="ButtonSize.Small"
                    Click="@(() => grid.CancelEditRow(data))" />
                </EditTemplate>
            </RadzenDataGridColumn>
        </Columns>
    </RadzenDataGrid>
}
else
{
    <p><em>No Data to display.</em></p>
}

@code {
    [Parameter] public List<ExpandoObject>? Items { get; set; }
    [Parameter] public int ItemsCount { get; set; }
    [Parameter] public int ItemsPerPage { get; set; }
    
    [Parameter] public EventCallback<ExpandoObject> OnSaveItem { get; set; }
    [Parameter] public Func<LoadDataArgs, Task<List<ExpandoObject>>>? OnLoadData { get; set; }

    
    Density Density = Density.Compact; 
    Radzen.DataGridGridLines GridLines = Radzen.DataGridGridLines.Both; 
    
    private RadzenDataGrid<ExpandoObject>? grid;

    private async Task LoadData(LoadDataArgs args)
    {
        if (OnLoadData is not null)
        {
            Items = await OnLoadData(args);
        }
        StateHasChanged(); 
    }

    private async Task OnUpdateRow(ExpandoObject item)
    {
        if (OnSaveItem.HasDelegate)
        {
            await OnSaveItem.InvokeAsync(item);
        }
    }
}

Log on execution

2025-06-02 09:25:13.080 +02:00 [INF] In test-dynamique-radzen LoadData, args.Filter is null or Empty

You can use the dynamic data approach from this demo:

Hello Enchev,
Thanks for answering.
Ok! i've used this example first in my test :wink: but could you confirm that the type of TiTem is the problem? if so why or anything else in my approch? Regards

I've not tried ExpandoObject and I cannot say if it's the problem or not.

Hi Enchev, I duplicated your demo and simply added my private async Task LoadData(LoadDataArgs args) method to retrieve data from my DBMS backend. I think the problem I'm having with ExpandoObject List also occurs if I don't use IEnumerable<MyEntity> to populate the data grid...
I've set up two pages with RadzenDataGrid. The first, let's say entirely static, uses IEnumerable<MyEntity>. Everything works fine, and when loaddata is called, here's the debug view of args.Filter passed to LoadData.

When I try a "dynamic version", second page, with your demo code and use IEnumerable<IDictionary<string, object>> to populate the RadzenDataGrid, the args.Filter content is different, as you can see, and throws an exception (previously incorrectly caught) when using System.Linq.Dynamic.Core.

if (!string.IsNullOrWhiteSpace(args.Filter)) { query = query.Where(args.Filter); }

Here's the exception traceback:

Could you please tell me how to work around this issue, or how to proceed to a fully dynamic version?
Thank you for your attention, regards

I’ve never seen such error before. Are you using latest version of Radzen.Blazor?

Hi Enchev,

Here is part of my .csproj

<ItemGroup>
    <PackageReference Include="Blazor.Bootstrap" Version="3.3.1" />
    <PackageReference Include="GitVersion.MsBuild" Version="6.3.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.AspNetCore.Authentication.Negotiate" Version="8.0.15" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.15" />
    <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.3" />
    <PackageReference Include="Radzen.Blazor" Version="6.6.3" />
    <PackageReference Include="Serilog" Version="4.2.0" />
    <PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
    <PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
    <PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
    <PackageReference Include="System.DirectoryServices.AccountManagement" Version="8.0.1" />
  </ItemGroup>

Regards

This is not the latest version.

Ok i'll upgrade to 7.0.7, i'm travelling today, will test as soon as possible !
Regards

Hi Enchev,

Here is the result using 7.0.7, clearly something has changed :laughing: but it seems not the syntax Linq await !

Raised exception:

Regards

Please post runnable code demonstrating this exception.

Hello Enchev,

Here is a code snippet heavily inspired by your demos that exposes my problem. I hope it will be easily adaptable to your test environment. The Exception show off on Console Log.

Regards.

@page "/radzenForum"

@using Radzen
@using Radzen.Blazor
@using Microsoft.EntityFrameworkCore
@using Microsoft.JSInterop
@using System.Text.Json
@using MySampleProject.Shared
@using MySampleProject.Data
@using MySampleProject.Data.Entities.Database
@using System.Reflection
@using System.Linq.Dynamic.Core;

@inject IJSRuntime JSRuntime
@inject NavigationManager NavigationManager
@inject IDbContextFactory<ApplicationDbContext> _contextFactory

<RadzenDataGrid @bind-Value=@selectedItems 
Data="@data" 
TItem="IDictionary<string, object>" 
Count="@count" 
LoadData=@LoadData
PageSize="@pageSize"
ColumnWidth="200px"
AllowFiltering="true" FilterPopupRenderMode="PopupRenderMode.OnDemand" FilterMode="FilterMode.Advanced" AllowPaging="true" AllowSorting="true">
    <Columns>
        @foreach (var column in columns)
        {
            <RadzenDataGridColumn @key=@column.Key Title="@column.Key" Type="column.Value"
            Property="@PropertyAccess.GetDynamicPropertyExpression(column.Key, column.Value)">
                <Template>
                    @context[@column.Key]
                </Template>
            </RadzenDataGridColumn>
        }
    </Columns>
</RadzenDataGrid>

<EventConsole @ref=@console />

@code {
    
    IList<IDictionary<string, object>>? selectedItems;
    IEnumerable<IDictionary<string, object>>? data { get; set; }
    IDictionary<string, Type> columns { get; set; } = new Dictionary<string, Type>() { };
	
	int pageSize = 4;
	int count;

    EventConsole console;

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        columns = GetEntityPropertyTypes<Employee>();
    }

    async Task LoadData(LoadDataArgs args)
    {
        using var dbContext = _contextFactory.CreateDbContext();
        var query = dbContext.Employees.AsQueryable();
        try
        {
            if (!string.IsNullOrEmpty(args.Filter))
            {
                query = query.Where(args.Filter);
            }
        }catch(Exception ex)
        {
            console.Log($"LoadData setting filters exception raised : {ex.ToString()}");
        }

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

        count = query.Count();

        var employees = await query.Skip(args.Skip ?? 0).Take(args.Top ?? 10).ToListAsync();
		data = ToDictionaryListAsync(employees);
    }
	
	Dictionary<string, Type> GetEntityPropertyTypes<TEntity>()
    {
        return typeof(TEntity)
            .GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .ToDictionary(prop => prop.Name, prop => prop.PropertyType);
    }

    IEnumerable<IDictionary<string, object>> ToDictionaryListAsync<TEntity>(List<TEntity> entities)
    {
        var props = typeof(TEntity).GetProperties(BindingFlags.Public | BindingFlags.Instance);

        return entities.Select(entity =>
        {
            var dict = new Dictionary<string, object?>();
            foreach (var prop in props)
            {
                dict[prop.Name] = prop.GetValue(entity);
            }
            return (IDictionary<string, object>)dict;
        });
    }

    TEntity ToEntity<TEntity>(IDictionary<string, object?> dict) where TEntity : new()
    {
        var entity = new TEntity();
        var props = typeof(TEntity).GetProperties(BindingFlags.Public | BindingFlags.Instance);

        foreach (var prop in props)
        {
            if (dict.TryGetValue(prop.Name, out var value) && value != null)
            {
                try
                {
                    var targetType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
                    var convertedValue = Convert.ChangeType(value, targetType);
                    prop.SetValue(entity, convertedValue);
                }
                catch
                {
                    // ToDo
                }
            }
        }
        return entity;
    }
}

I'm afraid that we cannot run this code - please post code that we can run.

Hello Enchev,

No problem I will try to do so :laughing: ! But what do you need or wich part of this code is a problem for you ?

FYI, for me, this trick permit to focus more precisly the problem ! a corected filter works ! but there is also the same formatting problem for args.OrderBy !

try
 {
     if (!string.IsNullOrEmpty(args.Filter))
     {
         query = query.Where(args.Filter);
     }
 } catch (Exception ex)
 {
     console!.Log(args.Filter.ToString());
     console!.Log($"LoadData setting filters exception raised : {ex.ToString()}");
     var correctedFilter = Regex.Unescape(args.Filter);
     correctedFilter = correctedFilter.Replace("[\"", ".").Replace("\"]", "");
     args.Filter = correctedFilter;
     try {
         query = query.Where(args.Filter);
     } catch (Exception nextEx) { 
         console!.Log($"LoadData setting filters exception raised after filter correction : {nextEx.ToString()}");
     }
 }

Regards

Hello Enchev,

If you want to see ! Go to your demo Blazor DataGrid Component - LoadData Event | Free UI Components by Radzen
Edit source, select and delete all, past code below:


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

@inherits DbContextPage

<RadzenDataGrid @bind-Value=@selectedItems 
Data="@data" 
TItem="IDictionary<string, object>" 
Count="@count" 
LoadData=@LoadData
PageSize="@pageSize"
ColumnWidth="200px"
AllowFiltering="true" FilterPopupRenderMode="PopupRenderMode.OnDemand" FilterMode="FilterMode.Advanced" AllowPaging="true" AllowSorting="true">
    <Columns>
        @foreach (var column in columns)
        {
            <RadzenDataGridColumn @key=@column.Key Title="@column.Key" Type="column.Value"
            Property="@PropertyAccess.GetDynamicPropertyExpression(column.Key, column.Value)">
                <Template>
                    @context[@column.Key]
                </Template>
            </RadzenDataGridColumn>
        }
    </Columns>
</RadzenDataGrid>

<EventConsole @ref=@console />

@code {
    int pageSize = 4;

    IList<IDictionary<string, object>>? selectedItems;
    IEnumerable<IDictionary<string, object>>? data { get; set; }
    IDictionary<string, Type> columns { get; set; } = GetEntityPropertyTypes<Employee>();

    EventConsole? console;
    int count;
    

    async Task LoadData(LoadDataArgs args)
    {
        var query = dbContext.Employees.AsQueryable();

        try
        {
            if (!string.IsNullOrEmpty(args.Filter))
            {
                query = query.Where(args.Filter);
            }
        } catch (Exception ex)
        {
            console!.Log(args.Filter.ToString());
            console!.Log($"LoadData setting filters exception raised : {ex.ToString()}");
            var correctedFilter = Regex.Unescape(args.Filter);
            correctedFilter = correctedFilter.Replace("[\"", ".").Replace("\"]", "");
            args.Filter = correctedFilter;
            try {
                query = query.Where(args.Filter);
            } catch (Exception nextEx) { 
                console!.Log($"LoadData setting filters exception raised after filter correction : {nextEx.ToString()}");
            }
        }

        try
        {
            if (!string.IsNullOrEmpty(args.OrderBy))
            {
                query = query.OrderBy(args.OrderBy);
            }
        }
        catch (Exception ex)
        {
            console!.Log(args.OrderBy.ToString());
            console!.Log($"LoadData setting order exception raised : {ex.ToString()}");
        }

        count = query!.Count();

        var employees = await query!.Skip(args.Skip ?? 0).Take(args.Top ?? 10).ToListAsync();
        data = ToDictionaryListAsync(employees);
    }
	
	static Dictionary<string, Type> GetEntityPropertyTypes<TEntity>()
    {
        return typeof(TEntity)
            .GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .ToDictionary(prop => prop.Name, prop => prop.PropertyType);
    }

    static IEnumerable<IDictionary<string, object>> ToDictionaryListAsync<TEntity>(List<TEntity> entities)
    {   
        var props = typeof(TEntity).GetProperties(BindingFlags.Public | BindingFlags.Instance);

        return entities.Select(entity =>
        {
            var dict = new Dictionary<string, object?>();
            foreach (var prop in props)
            {
                dict[prop.Name] = prop.GetValue(entity);
            }
            return (IDictionary<string, object>)dict;
        });
    }

    static TEntity ToEntity<TEntity>(IDictionary<string, object?> dict) where TEntity : new()
    {
        var entity = new TEntity();
        var props = typeof(TEntity).GetProperties(BindingFlags.Public | BindingFlags.Instance);

        foreach (var prop in props)
        {
            if (dict.TryGetValue(prop.Name, out var value) && value != null)
            {
                try
                {
                    var targetType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
                    var convertedValue = Convert.ChangeType(value, targetType);
                    prop.SetValue(entity, convertedValue);
                }
                catch
                {
                    // ToDo
                }
            }
        }
        return entity;
    }
}

Regards.

You convert the data to dictionary after Where() is applied - this is not going to work.

hum i dont think so; beware that it is data that is bind to the grid not employees ...
You could see that data load well on the online demo page, filter raise exception but works after correction, and order raise only exception !

Your data are bound to the DataGrid however you apply the filtering and sorting to the original collection which is not dictionary.

I'm sorry, but you lost me :slight_smile:
I understood that to be able to manage the datagrid in dynamic mode, you had to use a dictionary structure. Using the code in your demo, this is the case. I don't see any other way to link to the database data other than converting the data returned by the queries into a dictionary to refresh the variable linked to the datagrid, with the need to convert the data once the database access is complete! Would you be kind enough to tell me how to do it differently, or if there is a demo that addresses this particular case, which complements your generic Dadagrid demo.

Well, I don’t think I can add anything to what I already said and I don’t have any spare time to argue with you for obvious things. I’’m closing this thread.