Breaking change in 4.1.11 (DoropDown optimization) causes ArgumentException #591

Closed
opened 2026-01-29 17:39:50 +00:00 by claunia · 2 comments
Owner

Originally created by @OndrejUzovic on GitHub (Oct 22, 2022).

If the Data property of RadzenDropDown contains a reference to IEnumerable and not to generic IEnumerable<T> the following exception occurs when SetParameters is called:

[2022-10-22T08:54:15.164Z] Error: System.ArgumentException: 'CustomerID' is not a member of type 'System.Object' (Parameter 'propertyOrFieldName')
   at System.Linq.Expressions.Expression.PropertyOrField(Expression expression, String propertyOrFieldName)
   at Radzen.PropertyAccess.Getter[TItem,TValue](String propertyName, Type type) in C:\Ondrej\Source\Repos\Semafor\Radzen.Blazor\Common.cs:line 1884
   at Radzen.DropDownBase`1.OnParametersSet() in C:\Ondrej\Source\Repos\Semafor\Radzen.Blazor\DropDownBase.cs:line 382
   at Microsoft.AspNetCore.Components.ComponentBase.CallOnParametersSetAsync()
   at Radzen.RadzenComponent.<>n__0(ParameterView parameters)
   at Radzen.RadzenComponent.SetParametersAsync(ParameterView parameters) in C:\Ondrej\Source\Repos\Semafor\Radzen.Blazor\RadzenComponent.cs:line 189
   at Radzen.DataBoundFormComponent`1.SetParametersAsync(ParameterView parameters) in C:\Ondrej\Source\Repos\Semafor\Radzen.Blazor\DataBoundFormComponent.cs:line 305
   at Radzen.DropDownBase`1.SetParametersAsync(ParameterView parameters) in C:\Ondrej\Source\Repos\Semafor\Radzen.Blazor\DropDownBase.cs:line 789
   at Radzen.Blazor.RadzenDropDown`1.SetParametersAsync(ParameterView parameters) in C:\Ondrej\Source\Repos\Semafor\Radzen.Blazor\RadzenDropDown.razor.cs:line 124

Here is the simple example to reproduce the error:

@page "/"

@using Radzen;
@using Radzen.Blazor;
@using System.Linq.Dynamic.Core;
@using System.Collections;

<div style="width:700px; height:500px; background-color:beige;">
    <RadzenDropDown Data=@myLoadedData LoadData=@OnLoadData Count="@myVirtualizationCount"
                    @bind-Value=myValue 
                    ValueProperty="CustomerID" TextProperty="CompanyName"
                    Style="width:15rem"/>
</div>


@code {
    private class Record
    {
        public string CustomerID { get; set; }
        public string CompanyName { get; set; }
    }

    private int myVirtualizationCount;
    private IEnumerable myLoadedData;

    private IEnumerable myDropDownData;
    private string myValue;

    protected override void OnInitialized()
    {
        var items = new List<Record>();
        for (int i = 0; i < 100; ++i)
        {
            var record = new Record() { CustomerID = i.ToString(), CompanyName = $"Name{i}" };
            items.Add(record);
        }

        myDropDownData = items;
    }

    private void OnLoadData(LoadDataArgs args)
    {
        var query = myDropDownData.AsQueryable();

        // Do some logic based on Queryable.
        // ... e.g. special filtering etc.

        myVirtualizationCount = query.Count();

        myLoadedData = query.AsEnumerable();
    }
}

The example above is very simplified, just to demonstrate the problem.
But the possibility to use IEnumerable instead of the generic IEnumerable<T> is important.
E.g. to be able to wrap RadzenDropDown into a more specific DropDown which implements some business logic (e.g. special filtering) but still needs to be able to take any type of Data (same way like RadzenDropDown takes via the non-generic IEnumerable Data property).

Please, could you preserve the compatility with the previous behavior?

Originally created by @OndrejUzovic on GitHub (Oct 22, 2022). If the Data property of RadzenDropDown contains a reference to `IEnumerable` and not to generic `IEnumerable<T>` the following exception occurs when SetParameters is called: ``` [2022-10-22T08:54:15.164Z] Error: System.ArgumentException: 'CustomerID' is not a member of type 'System.Object' (Parameter 'propertyOrFieldName') at System.Linq.Expressions.Expression.PropertyOrField(Expression expression, String propertyOrFieldName) at Radzen.PropertyAccess.Getter[TItem,TValue](String propertyName, Type type) in C:\Ondrej\Source\Repos\Semafor\Radzen.Blazor\Common.cs:line 1884 at Radzen.DropDownBase`1.OnParametersSet() in C:\Ondrej\Source\Repos\Semafor\Radzen.Blazor\DropDownBase.cs:line 382 at Microsoft.AspNetCore.Components.ComponentBase.CallOnParametersSetAsync() at Radzen.RadzenComponent.<>n__0(ParameterView parameters) at Radzen.RadzenComponent.SetParametersAsync(ParameterView parameters) in C:\Ondrej\Source\Repos\Semafor\Radzen.Blazor\RadzenComponent.cs:line 189 at Radzen.DataBoundFormComponent`1.SetParametersAsync(ParameterView parameters) in C:\Ondrej\Source\Repos\Semafor\Radzen.Blazor\DataBoundFormComponent.cs:line 305 at Radzen.DropDownBase`1.SetParametersAsync(ParameterView parameters) in C:\Ondrej\Source\Repos\Semafor\Radzen.Blazor\DropDownBase.cs:line 789 at Radzen.Blazor.RadzenDropDown`1.SetParametersAsync(ParameterView parameters) in C:\Ondrej\Source\Repos\Semafor\Radzen.Blazor\RadzenDropDown.razor.cs:line 124 ``` Here is the simple example to reproduce the error: ``` @page "/" @using Radzen; @using Radzen.Blazor; @using System.Linq.Dynamic.Core; @using System.Collections; <div style="width:700px; height:500px; background-color:beige;"> <RadzenDropDown Data=@myLoadedData LoadData=@OnLoadData Count="@myVirtualizationCount" @bind-Value=myValue ValueProperty="CustomerID" TextProperty="CompanyName" Style="width:15rem"/> </div> @code { private class Record { public string CustomerID { get; set; } public string CompanyName { get; set; } } private int myVirtualizationCount; private IEnumerable myLoadedData; private IEnumerable myDropDownData; private string myValue; protected override void OnInitialized() { var items = new List<Record>(); for (int i = 0; i < 100; ++i) { var record = new Record() { CustomerID = i.ToString(), CompanyName = $"Name{i}" }; items.Add(record); } myDropDownData = items; } private void OnLoadData(LoadDataArgs args) { var query = myDropDownData.AsQueryable(); // Do some logic based on Queryable. // ... e.g. special filtering etc. myVirtualizationCount = query.Count(); myLoadedData = query.AsEnumerable(); } } ``` The example above is very simplified, just to demonstrate the problem. But the possibility to use `IEnumerable` instead of the generic `IEnumerable<T>` is important. E.g. to be able to wrap RadzenDropDown into a more specific DropDown which implements some business logic (e.g. special filtering) but still needs to be able to take any type of `Data` (same way like RadzenDropDown takes via the non-generic IEnumerable Data property). Please, could you preserve the compatility with the previous behavior?
Author
Owner

@MPapst commented on GitHub (Oct 22, 2022):

The example above is very simplified, just to demonstrate the problem. But the possibility to use IEnumerable instead of the generic IEnumerable<T> is important. E.g. to be able to wrap RadzenDropDown into a more specific DropDown which implements some business logic (e.g. special filtering) but still needs to be able to take any type of Data (same way like RadzenDropDown takes via the non-generic IEnumerable Data property).

When creating a wrapping component, you can just forward the generic type parameter by creating a component with a typeparam.
This also will bring some type safety to your own business logic (e.g. by creating validation services that take type parameters)

@MPapst commented on GitHub (Oct 22, 2022): > The example above is very simplified, just to demonstrate the problem. But the possibility to use `IEnumerable` instead of the generic `IEnumerable<T>` is important. E.g. to be able to wrap RadzenDropDown into a more specific DropDown which implements some business logic (e.g. special filtering) but still needs to be able to take any type of `Data` (same way like RadzenDropDown takes via the non-generic IEnumerable Data property). When creating a wrapping component, you can just forward the generic type parameter by creating a component with a typeparam. This also will bring some type safety to your own business logic (e.g. by creating validation services that take type parameters)
Author
Owner

@OndrejUzovic commented on GitHub (Oct 22, 2022):

The example above is very simplified, just to demonstrate the problem. But the possibility to use IEnumerable instead of the generic IEnumerable<T> is important. E.g. to be able to wrap RadzenDropDown into a more specific DropDown which implements some business logic (e.g. special filtering) but still needs to be able to take any type of Data (same way like RadzenDropDown takes via the non-generic IEnumerable Data property).

When creating a wrapping component, you can just forward the generic type parameter by creating a component with a typeparam. This also will bring some type safety to your own business logic (e.g. by creating validation services that take type parameters)

Hm, actually you are right. Introducing the second @typeparam TDropItem and then making the Data property of type IEnumerable<TDropItem> seems much better approach for the wrapping than reusing that non-generic IEnumerable pattern.

Is there any reason why Radzen components use non-generic IEnumerable Data instead of the generic IEnumerable<..> Data via typeparam?
(Or is it just a historical inheritance?)

@OndrejUzovic commented on GitHub (Oct 22, 2022): > > The example above is very simplified, just to demonstrate the problem. But the possibility to use `IEnumerable` instead of the generic `IEnumerable<T>` is important. E.g. to be able to wrap RadzenDropDown into a more specific DropDown which implements some business logic (e.g. special filtering) but still needs to be able to take any type of `Data` (same way like RadzenDropDown takes via the non-generic IEnumerable Data property). > > When creating a wrapping component, you can just forward the generic type parameter by creating a component with a typeparam. This also will bring some type safety to your own business logic (e.g. by creating validation services that take type parameters) Hm, actually you are right. Introducing the second `@typeparam TDropItem` and then making the Data property of type `IEnumerable<TDropItem>` seems much better approach for the wrapping than reusing that non-generic `IEnumerable` pattern. Is there any reason why Radzen components use non-generic `IEnumerable Data` instead of the generic `IEnumerable<..> Data` via `typeparam`? (Or is it just a historical inheritance?)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/radzen-blazor#591