You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MudBlazor DataGrid动态自定义列过滤报错:Object无对应属性

MudBlazor DataGrid动态列过滤失败解决方案

问题背景

在MudBlazor DataGrid中实现了自定义列组件,用于动态展示数据并支持排序,排序功能正常,但应用过滤时抛出错误:

"Instance property 'Name' is not defined for type 'System.Object'"

原因是自定义列的PropertyExpression生成Lambda表达式时,将数据视为System.Object类型(MudDataGrid<T>的T设为了object),无法识别动态生成类型的属性。当前使用System.Reflection.Emit创建动态类型,需要让过滤功能兼容动态列。

错误堆栈

Unhandled exception in circuit 'ZrG_nxlCYQm_E9s02ePEHPq3brotbidZKxPtIHYph_0'.
System.ArgumentException: Instance property 'Name' is not defined for type 'System.Object' (Parameter 'propertyName')
   at System.Linq.Expressions.Expression.Property(Expression expression, String propertyName)
   at CustomColumnDemo.Components.Pages.DynamicData.CustomColumn`1.get_PropertyExpression() in D:\C#\CustomColumnDemo\CustomColumnDemo\Components\Pages\DynamicData\CustomColumn.cs:line 36
   at MudBlazor.FilterDefinition`1.GenerateFilterFunction(FilterOptions filterOptions)
   at MudBlazor.MudDataGrid`1.get_FilteredItems()
   at MudBlazor.MudDataGrid`1.get_CurrentPageItems()
   at MudBlazor.MudDataGrid`1.<BuildRenderTree>b__520_6(RenderTreeBuilder __builder3)
   at Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddContent(Int32 sequence, RenderFragment fragment)
   at MudBlazor.MudDropContainer`1.<BuildRenderTree>b__94_0(RenderTreeBuilder __builder2)
   at Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddContent(Int32 sequence, RenderFragment fragment)
   at Microsoft.AspNetCore.Components.CascadingValue`1.Render(RenderTreeBuilder builder)
   at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment, Exception& renderFragmentException)

现有代码

Razor组件代码

<MudDataGrid T="object" Items="@dynamicObjects" Hover="true" Bordered="true" Dense="true" MultiSelection="false" SortMode="SortMode.Multiple" Filterable="true">
    <Columns>
        @if (DynamicProperties != null)
        {
            @foreach (var property in DynamicProperties)
            {
                <CustomColumn T="object"
                              Title="@property.Name"
                              Field="@property.Name"
                              CustomPropertyType="@property.PropertyType" />
            }
        }
    </Columns>
</MudDataGrid>
@code {
    private Type dynamicType;
    private List<object> dynamicObjects = new();
    private List<PropertyInfo> DynamicProperties = new();

    protected override void OnInitialized()
    {
        // 定义动态属性
        var properties = new Dictionary<string, Type>
        {
            { "Name", typeof(string) },
            { "Age", typeof(int) },
            { "Country", typeof(string) }
        };

        // 创建动态类型
        dynamicType = CreateDynamicType("DynamicClass", properties);

        // 保存属性用于渲染列
        DynamicProperties = dynamicType.GetProperties().ToList();

        // 创建动态对象
        dynamicObjects = new List<object>
        {
            CreateDynamicObject(dynamicType, new Dictionary<string, object> { { "Name", "Alice" }, { "Age", 30 }, { "Country", "USA" } }),
            CreateDynamicObject(dynamicType, new Dictionary<string, object> { { "Name", "Bob" }, { "Age", 25 }, { "Country", "Canada" } }),
            CreateDynamicObject(dynamicType, new Dictionary<string, object> { { "Name", "Charlie" }, { "Age", 35 }, { "Country", "UK" } })
        };
    }

    private Type CreateDynamicType(string typeName, Dictionary<string, Type> properties)
    {
        // 动态类型创建逻辑
        var assemblyName = new AssemblyName("DynamicAssembly");
        var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
        var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
        var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);

        foreach (var property in properties)
        {
            var fieldBuilder = typeBuilder.DefineField($"_{property.Key}", property.Value, FieldAttributes.Private);

            var propertyBuilder = typeBuilder.DefineProperty(property.Key, PropertyAttributes.HasDefault, property.Value, null);

            var getter = typeBuilder.DefineMethod(
                $"get_{property.Key}",
                MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
                property.Value,
                Type.EmptyTypes);

            var getterIL = getter.GetILGenerator();
            getterIL.Emit(OpCodes.Ldarg_0);
            getterIL.Emit(OpCodes.Ldfld, fieldBuilder);
            getterIL.Emit(OpCodes.Ret);

            var setter = typeBuilder.DefineMethod(
                $"set_{property.Key}",
                MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
                null,
                new[] { property.Value });

            var setterIL = setter.GetILGenerator();
            setterIL.Emit(OpCodes.Ldarg_0);
            setterIL.Emit(OpCodes.Ldarg_1);
            setterIL.Emit(OpCodes.Stfld, fieldBuilder);
            setterIL.Emit(OpCodes.Ret);

            propertyBuilder.SetGetMethod(getter);
            propertyBuilder.SetSetMethod(setter);
        }

        return typeBuilder.CreateType();
    }

    private object CreateDynamicObject(Type dynamicType, Dictionary<string, object> propertyValues)
    {
        var instance = Activator.CreateInstance(dynamicType);

        foreach (var property in propertyValues)
        {
            dynamicType.GetProperty(property.Key)?.SetValue(instance, property.Value);
        }

        return instance;
    }
}

自定义列代码

public class CustomColumn<T> : Column<T>
{
    [Parameter]
    public string Field { get; set; }

    [Parameter]
    public Type CustomPropertyType { get; set; }

    protected override Type PropertyType => CustomPropertyType ?? base.PropertyType;

    protected override object CellContent(T item)
    {
        if (item != null && !string.IsNullOrEmpty(Field))
        {
            var property = item.GetType().GetProperty(Field);
            return property?.GetValue(item);
        }
        return null;
    }

    public override string PropertyName => Field;

    protected internal virtual LambdaExpression? PropertyExpression {
        get
        {
            var parameter = Expression.Parameter(typeof(T), "x");
            
            var property = Expression.Property(parameter, Field);
            
            var lambda = Expression.Lambda(property, parameter);
            
            return lambda;
        }
    }

    protected override object PropertyFunc(T item)
    {
        if (item != null && !string.IsNullOrEmpty(Field))
        {
            var property = item.GetType().GetProperty(Field);
            return property?.GetValue(item);
        }
        return null;
    }

    protected override void SetProperty(object item, object value)
    {
        if (item != null && !string.IsNullOrEmpty(Field))
        {
            var property = typeof(T).GetProperty(Field);
            if (property != null && property.CanWrite)
            {
                property.SetValue(item, value);
            }
        }
    }
}

解决方案

方案1:修改PropertyExpression实现(优先推荐)

问题核心是ParameterExpression类型为object,无法直接访问动态类型属性。通过构建反射调用的表达式,绕过编译时类型检查:

修改CustomColumn<T>的PropertyExpression属性:

protected internal override LambdaExpression? PropertyExpression
{
    get
    {
        if (string.IsNullOrEmpty(Field)) return null;

        var parameter = Expression.Parameter(typeof(T), "x");

        // 构建表达式:x.GetType().GetProperty(Field).GetValue(x)
        var getTypeMethod = typeof(object).GetMethod(nameof(object.GetType))!;
        var getTypeExpr = Expression.Call(parameter, getTypeMethod);

        var getPropertyMethod = typeof(Type).GetMethod(nameof(Type.GetProperty), new[] { typeof(string) })!;
        var propertyNameExpr = Expression.Constant(Field);
        var getPropertyExpr = Expression.Call(getTypeExpr, getPropertyMethod, propertyNameExpr);

        var getValueMethod = typeof(PropertyInfo).GetMethod(nameof(PropertyInfo.GetValue), new[] { typeof(object) })!;
        var getValueExpr = Expression.Call(getPropertyExpr, getValueMethod, parameter);

        // 转换为属性实际类型
        var convertedExpr = Expression.Convert(getValueExpr, CustomPropertyType);

        var lambda = Expression.Lambda(convertedExpr, parameter);
        return lambda;
    }
}

该实现通过反射调用获取动态属性值,无需依赖编译时类型信息,直接兼容动态生成的类型。

方案2:反射实例化泛型MudDataGrid

如果动态类型在初始化时已知,可通过反射创建MudDataGrid<T>实例(T为动态类型),让组件直接感知动态类型:

protected override void OnInitialized()
{
    // ... 原有动态类型创建逻辑 ...

    // 反射创建泛型MudDataGrid
    var dataGridType = typeof(MudDataGrid<>).MakeGenericType(dynamicType);
    var dataGrid = Activator.CreateInstance(dataGridType);
    
    // 设置Items属性
    var itemsProperty = dataGridType.GetProperty(nameof(MudDataGrid<object>.Items));
    itemsProperty?.SetValue(dataGrid, dynamicObjects);
    
    // 后续可将dataGrid添加到组件树中(需手动处理渲染)
}

此方式能让MudDataGrid原生支持动态类型的过滤和排序,但需要手动构建组件渲染逻辑,适合复杂动态场景。

方案3:自定义过滤逻辑

绕过MudBlazor内置过滤机制,完全手动实现:

  1. 设置MudDataGrid的Filterable=false,隐藏默认过滤框
  2. 自定义过滤UI,收集用户输入的过滤条件
  3. 在代码中手动过滤dynamicObjects集合,更新绑定的数据源

该方式完全控制过滤逻辑,避免Lambda表达式的类型问题,但需要额外开发过滤UI和逻辑。

总结

优先选择方案1,仅修改PropertyExpression的实现即可兼容动态列过滤,无需改动整体架构;若需要更原生的组件支持,可考虑方案2;方案3适合需要高度定制过滤逻辑的场景。

内容的提问来源于stack exchange,提问作者Utkarsh Shinde

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 18:37:02