MudBlazor DataGrid动态自定义列过滤报错:Object无对应属性
问题背景
在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内置过滤机制,完全手动实现:
- 设置
MudDataGrid的Filterable=false,隐藏默认过滤框 - 自定义过滤UI,收集用户输入的过滤条件
- 在代码中手动过滤
dynamicObjects集合,更新绑定的数据源
该方式完全控制过滤逻辑,避免Lambda表达式的类型问题,但需要额外开发过滤UI和逻辑。
总结
优先选择方案1,仅修改PropertyExpression的实现即可兼容动态列过滤,无需改动整体架构;若需要更原生的组件支持,可考虑方案2;方案3适合需要高度定制过滤逻辑的场景。
内容的提问来源于stack exchange,提问作者Utkarsh Shinde

