基于TModel与属性名创建Expression时遇类型错误,如何解决?
解决从TModel和字符串属性名创建
Expression(Of Func(Of TModel, TResult))适配Mvc EditorFor的问题 问题背景
需求是通过TModel和字符串属性名构建Expression(Of Func(Of TModel, TResult)),用于Mvc的EditorFor方法,但两次尝试均报错:
尝试方案1:直接构建Lambda
报错信息:Expression of type 'System.Boolean' cannot be used for return type 'System.Object'
代码示例:
<Extension> Public Function ModelEditor(Of TModel)(html As HtmlHelper(Of TModel)) As MvcHtmlString Dim meta = html.ViewData.ModelMetadata Dim htmlString As New StringBuilder() Dim paramExp = Expression.Parameter(GetType(TModel), "model") For Each editor In meta.Properties Dim memberExp = Expression.Property(paramExp, editor.PropertyName) Dim exp = Expression.Lambda(Of Func(Of TModel, Object))(memberExp, paramExp) htmlString.Append(html.EditorFor(exp)) Next Return MvcHtmlString.Create(htmlString.ToString()) End Function
尝试方案2:添加类型转换
报错信息:Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.
代码示例:
Dim memberExp = Expression.Property(paramExp, editor.PropertyName) ' Convert to a Object Dim convertExp = Expression.Convert(memberExp, GetType(Object)) Dim exp = Expression.Lambda(Of Func(Of TModel, Object))(convertExp, paramExp) htmlString.Append(html.EditorFor(exp))
问题原因
- 方案1:当属性是值类型(如Boolean、Int32)时,
Expression.Property返回值类型表达式,但Func(Of TModel, Object)要求返回引用类型,值类型未装箱导致类型不匹配。 - 方案2:添加
Expression.Convert后,Lambda主体表达式类型变为Convert,不再是Mvc要求的MemberAccess类型,触发模板验证失败。
解决方案
核心思路是根据属性实际类型创建对应泛型版本的Expression(Of Func(Of TModel, TResult)),通过反射调用EditorFor的泛型重载,既避免统一用Object导致的类型问题,又保证表达式符合Mvc的验证要求。
实现代码:
<Extension> Public Function ModelEditor(Of TModel)(html As HtmlHelper(Of TModel)) As MvcHtmlString Dim meta = html.ViewData.ModelMetadata Dim htmlString As New StringBuilder() Dim paramExp = Expression.Parameter(GetType(TModel), "model") ' 获取EditorFor的泛型方法定义(单个Expression参数的重载) Dim editorForMethod = GetType(HtmlHelper(Of TModel)).GetMethods() _ .FirstOrDefault(Function(m) m.Name = "EditorFor" AndAlso m.GetParameters().Length = 1) For Each propMeta In meta.Properties ' 获取当前属性的实际类型 Dim propType = propMeta.PropertyType ' 创建对应类型的MemberAccess表达式 Dim memberExp = Expression.Property(paramExp, propMeta.PropertyName) ' 构建泛型Func类型及Lambda表达式 Dim lambdaFuncType = GetType(Func(Of ,)).MakeGenericType(GetType(TModel), propType) Dim lambdaExp = Expression.Lambda(lambdaFuncType, memberExp, paramExp) ' 调用泛型版本的EditorFor方法 Dim editorHtml = editorForMethod.MakeGenericMethod(propType).Invoke(html, {lambdaExp}) htmlString.Append(editorHtml.ToString()) Next Return MvcHtmlString.Create(htmlString.ToString()) End Function
代码说明
- 先获取
EditorFor的泛型方法定义,确保匹配接收单个Expression参数的重载。 - 遍历属性时,针对每个属性的实际类型构建Lambda表达式,主体为纯
MemberAccess类型,符合Mvc的模板验证规则。 - 通过反射调用泛型版本的
EditorFor,传入对应类型的Lambda表达式,生成正确的编辑器HTML。
内容的提问来源于stack exchange,提问作者simo026q
相关产品推荐
相关产品推荐

