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

基于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

代码说明

  1. 先获取EditorFor的泛型方法定义,确保匹配接收单个Expression参数的重载。
  2. 遍历属性时,针对每个属性的实际类型构建Lambda表达式,主体为纯MemberAccess类型,符合Mvc的模板验证规则。
  3. 通过反射调用泛型版本的EditorFor,传入对应类型的Lambda表达式,生成正确的编辑器HTML。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 06:40:34