当前位置: 首页 > 知识库问答 >
问题:

动态表示返回类型Lambda表达式

孔安福
2023-03-14

我有一个静态的通用FormBuilder超文本标记语言助手方法(HTMLHelper类上的扩展方法),它接受视图模型类型的通用参数,然后当从数据库传递一个或多个字符串属性名称时,生成一个超文本标记语言形式在ASP. NET MVC 5.1 with. NET 4.5中。

我有一个公共方法来生成表单,还有单独的私有方法来生成表单中的“模块”部分,然后渲染其中的每个字段。类型参数从上到下沿此链传递。

在“RenderField”方法中,我使用代码创建了一个类型化的HtmlHelper-

var typedHelper = helper as HtmlHelper<TModel>;

其中helper是RenderForm方法中扩展的HtmlHelper。

然后使用代码创建一个表达式-

var modelType = typeof(TModel);

...

var modelProperty = modelType.GetProperty(field.PropertyName);

if (modelProperty == null)
{
    Elmah.ErrorSignal.FromCurrentContext().Raise(new ArgumentException(string.Format("Model {0} does not contain property {1}", modelType.Name, field.PropertyName)));
    return null;
}

var modelPropertyType = modelProperty.PropertyType;

var parameter = Expression.Parameter(modelType, "m");

var property = Expression.Property(parameter, field.PropertyName);

var expression = Expression.Lambda<Func<TModel, object>>(property, parameter);

然后,我可以使用它创建EditorFor、DisplayFor或ValidationMessageFor,如下所示-

fieldContainer.InnerHtml += typedHelper.EditorFor(expression);
fieldContainer.InnerHtml += typedHelper.ValidationMessageFor(expression);

这适用于字符串编辑器,但如果我尝试使用可为null的datetime,则会出现错误-

类型'System. Nullable'1[System. DateTime]'的表达式不能用于返回类型'System. Object'

如果我尝试将属性转换为对象,正如我在Jon Skeet答案中看到的那样,通过更改以下行-

var expression = Expression.Lambda<Func<TModel, object>>(Expression.Convert(property, typeof(object)), parameter);

并将编辑器代码更改为-

var compiledExpression = expression.Compile()(model);

fieldContainer.InnerHtml += typedHelper.EditorFor(compiledExpression);
fieldContainer.InnerHtml += typedHelper.ValidationMessageFor(compiledExpression);

我收到错误消息,无法从用法中推断类型参数。

如果我将返回类型更改为“动态”,那么它会声明“无法动态调度扩展方法”。

我不能将“modelPropertyType”指定为泛型方法的返回参数——可能是因为它不能保证在编译时是具体类型。

是否有任何方法可以动态地将表达式的返回类型指定为运行时的属性类型,以便我可以使用ASP. NET MVC提供的EditorFor帮助器方法?

共有1个答案

蓝侯林
2023-03-14

假设您创建了一个helper函数,该函数具有TModel和TProperty的泛型参数,您可以在其中构建html以编辑单个模型属性。该助手将收到HtmlHelper的实例

private static MvcHtmlString GetPropertyEditor<TModel, TProperty>(HtmlHelper<TModel> htmlHelper, PropertyInfo propertyInfo)
{
    //Get property lambda expression like "m => m.Property"
    var modelType = typeof(TModel);
    var parameter = Expression.Parameter(modelType, "m");
    var property = Expression.Property(parameter, propertyInfo.Name);
    var propertyExpression = Expression.Lambda<Func<TModel, TProperty>>(property, parameter);

    //Get html string with label, editor and validation message
    var editorContainer = new TagBuilder("div");
    editorContainer.AddCssClass("editor-container");
    editorContainer.InnerHtml += htmlHelper.LabelFor(propertyExpression);
    editorContainer.InnerHtml += htmlHelper.EditorFor(propertyExpression);
    editorContainer.InnerHtml += htmlHelper.ValidationMessageFor(propertyExpression);
    return new MvcHtmlString(editorContainer.ToString());
}

该助手将生成一个容器div

如您所见,助手仍然要求您提供属性的泛型类型,因为您正在使用反射来循环每个属性,所以您将无法获得该泛型类型。但是,您也可以使用反射为每个属性调用此助手:

foreach (var propertyInfo in modelType.GetProperties())
{
    var openMethod = typeof(HtmlExtensions).GetMethod("GetPropertyEditor", BindingFlags.Static | BindingFlags.NonPublic);
    var genericMethod = openMethod.MakeGenericMethod(modelType, propertyInfo.PropertyType);
    var editorHtml = genericMethod.Invoke(null, new object[] { htmlHelper, propertyInfo });
    //add editorHtml to the form

}

因此,您可以创建自己的HtmlHelper扩展方法,为给定模型生成表单:

public static MvcHtmlString RenderForm<TModel>(this HtmlHelper<TModel> htmlHelper)
{
    var modelType = typeof(TModel);
    var form = new TagBuilder("form");
    foreach (var propertyInfo in modelType.GetProperties())
    {
        //call generic GetPropertyEditor<TModel, TProperty> with the type of this property
        var openMethod = typeof(HtmlExtensions).GetMethod("GetPropertyEditor", BindingFlags.Static | BindingFlags.NonPublic);
        var genericMethod = openMethod.MakeGenericMethod(modelType, propertyInfo.PropertyType);
        var editorHtml = genericMethod.Invoke(null, new object[] { htmlHelper, propertyInfo });
        //add the html to the form
        form.InnerHtml += editorHtml;
    }

    return new MvcHtmlString(form.ToString());
}

给出如下模型:

public class RegisterViewModel
{
    [Required]
    [Display(Name = "User name")]
    public string UserName { get; set; }

    [Required]
    [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [System.ComponentModel.DataAnnotations.Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
    public string ConfirmPassword { get; set; }

    public DateTime? RegisterDate { get; set; }
}

您可以在如下视图中使用它:

@Html.RenderForm()

它将生成以下html:

<form>
    <div class="editor-container">
        <label for="UserName">User name</label>
        <input class="text-box single-line" data-val="true" data-val-required="The User name field is required." id="UserName" name="UserName" type="text" value="" /><span class="field-validation-valid" data-valmsg-for="UserName" data-valmsg-replace="true"></span>
    </div>
    <div class="editor-container">
        <label for="Password">Password</label>
        <input class="text-box single-line password" data-val="true" data-val-length="The Password must be at least 6 characters long." data-val-length-max="100" data-val-length-min="6" data-val-required="The Password field is required." id="Password" name="Password" type="password" value="" /><span class="field-validation-valid" data-valmsg-for="Password" data-valmsg-replace="true"></span>
    </div>
    <div class="editor-container">
        <label for="ConfirmPassword">Confirm password</label>
        <input class="text-box single-line password" data-val="true" data-val-equalto="&#39;Confirm password&#39; and &#39;Password&#39; do not match." data-val-equalto-other="*.Password" id="ConfirmPassword" name="ConfirmPassword" type="password" value="" /><span class="field-validation-valid" data-valmsg-for="ConfirmPassword" data-valmsg-replace="true"></span>
    </div>
    <div class="editor-container">
        <label for="RegisterDate">RegisterDate</label>
        <input class="text-box single-line" data-val="true" data-val-date="The field RegisterDate must be a date." id="RegisterDate" name="RegisterDate" type="datetime" value="" /><span class="field-validation-valid" data-valmsg-for="RegisterDate" data-valmsg-replace="true"></span>
    </div>
</form>

您将对生成的html的结构、类名、属性等有不同的要求,但我希望这可以帮助您完成正在编写的FormBuilder!

 类似资料:
  • 将异常获取为 :lambda表达式中的返回类型错误:

  • 以下代码在IntelliJ和Eclipse中编译得很好,但JDK编译器1.8.0\u 25对此表示不满。首先,代码。 javac 1.8.0\u 25的输出为: 当我更换时?超级E只需使用E,JDK就能成功编译。 当我将替换为,JDK编译成功。 由于它适用于JDK 1.8.0_60,我怀疑它是编译器错误。 有没有详细说明是什么原因造成的以及何时修复的?

  • 我正在学习Java中的Lambda并试图理解它。例如,我有以下代码: 我不理解这个语句我看到是类型的引用变量。但是我不明白这个到底是什么。这是方法定义吗?这个方法的返回类型是什么?我认为应该是,因为方法的返回类型是。如有任何反馈将不胜感激。

  • 我正在编写一个lambda表达式来将给定的纬度和经度转换为地址。表达式应该以坐标为参数,并返回其相应的地址。但是,返回的值为null。以下是我的班级: 以下是logcat的输出: 以下是我的用法: 是类的对象,以下是相关接口: 当我尝试从相关适配器打印坐标时,它们会正确打印。因此,位置正在正确设置,但是当我试图从另一个类访问它时,它会显示字符串的空值。你能建议一个替代方法来从表达式中提取位置吗?

  • 我已经为使用者接口尝试了类似这样的lambda表达式,它没有问题。其他一些lambda表达式不起作用,因为它们返回一些东西,如。 编译和执行没有问题。 所以我遇到的问题是,我认为像这样的lambda表达式总是等效于,所以我希望编译器会给我一个错误,因为您不能从使用者接口返回(或任何其他类型)。显然,编译器正在将lambda表达式转换为一个方法,如果没有return语句,并且调用方法而不实际返回值。

  • 为什么会发生异常'lambda表达式中的ad返回类型:AuthenticatedUser无法转换为User'?如果可选为空,我只想返回此AuthenticatedUser。