【问题标题】:Template error when refining a lambda expression for use in MVC InputFor精炼 lambda 表达式以在 MVC InputFor 中使用时出现模板错误
【发布时间】:2017-09-04 12:09:17
【问题描述】:

在我的应用程序中,我有几个 (>10) 个地方有这样的模型:

public interface IOptionList
{
    string Name;
    bool Checked;
}

public class Option : IOptionList { }

public class MyModel
{
    public Option[] Options = 
    {
        new Option { Name = "Option 1", Checked = false }
    };

    // etc... for many more implementations of IOptionList
}

这些用于在视图中生成 CheckboxLists,如下所示:

@for (int i = 0; i < Model.Options.Length; i++)
{
    <div>
        @Html.CheckBoxFor(x => x.Options[i].Checked)
        @Html.LabelFor(x => x.Options[i].Checked, Model.Options[i].Name)
    </div>
}

因为它被使用了很多次,我想通过编写一个HtmlHelper 扩展来简化我的模型来生成这样的列表:

@Html.CheckBoxFormGroupFor(x => x.Options, Model.Options)

我目前的尝试是这样的:

public static CheckBoxFormGroupFor<TModel, TItem>(this HtmlHelper<TModel>html,
    Expression<Func<TModel, TItem[]>> expression, TItem[] values)
{
    var sb = new StringBuilder();

    for (int i = 0; i < values.Length; i++)
    {
        var indexExpression = Expression.ArrayIndex(expression.Body, Expression.Constant(i));
        var checkedAccessExpression = Expression.Property(indexExpression, typeof(ICheckboxList), "Checked");
        var lambda = Expression.Lambda<Func<TModel, bool>>(checkedAccessExpression, Expression.Parameter(typeof(TModel)));
        var cbx = html.CheckBoxFor(x => lambda.Compile()(x));
        var lbl = html.LabelFor(x => lambda.Compile()(x), values[i].Name);

        var div = new TagBuilder("div");
        div.InnerHtml = $"{cbx}{lbl}";
        sb.Append(div);
    }
    return new HtmlString(sb.ToString());
}

据我了解,它扩展了初始 lambda 表达式 x =&gt; x.Options 以在正确的数组索引处访问对象的正确属性,但是这给了我一个带有消息的模板错误

模板只能用于字段访问、属性访问、一维数组索引或单参数自定义索引器表达式。

据我所知,我只做一个单 D 数组索引,然后是属性访问,所以我不确定我为什么会看到这个。我之前尝试过var cbx = html.CheckBoxFor(lambda);,但这不起作用,因为参数x 仅在视图范围内定义。

假设我想要的甚至是可能的,任何人都可以帮助实现它吗?我不熟悉以这种方式操作Expressions

【问题讨论】:

  • 不清楚您想要传递给该方法的内容 - 您当前在两个参数中传递相同的模型。我无论如何,为什么不使用EditorTempate 和简单的@Html.EditorFor(m =&gt; m.Options) (错误是因为x =&gt; lambda.Compile()(x) 这没有任何意义,但又一次,不知道你想用它来实现什么
  • 你可以用一半的代码来做这一切,如果你总是传递一个IOptionList的数组,签名应该只是public static CheckBoxFormGroupFor&lt;TModel,&gt;(this HtmlHelper&lt;TModel&gt; html, Expression&lt;Func&lt;TModel, IEnumerable&lt;IOptionList&gt;&gt;&gt; expression)
  • 是的,我考虑过使用IEnumerable&lt;IOptionList&gt;,但是为了索引列表,我需要调用ToArrayExpression.ArrayIndex only 适用于数组)和模板对于 MVC 的 *For 方法不能包含方法调用 - 它会破坏模型绑定。
  • 您可以使其正常工作(与使用 ModelMetadata 的内置 HtmlHelper 方法的工作方式相同)。但是仍然不清楚为什么你不只为Option 类型创建一个EditorTemplate(只包含@Html.CheckBoxFor(x =&gt; x.Checked)@Html.LabelFor(x =&gt; x.Checked, Model.Name))而只使用@Html.EditorFor(m =&gt; m.Options)

标签: c# asp.net-mvc lambda linq-expressions


【解决方案1】:

你很接近。直接使用lambda 变量

var cbx = html.CheckBoxFor(lambda);
var lbl = html.LabelFor(lambda, values[i].Name);

确实是正确的方法。只需确保您正在编写的 lambda 表达式使用与 expression 参数相同的参数(因为它绑定到新表达式中使用的主体),即此处

var lambda = Expression.Lambda<Func<TModel, bool>>(
    checkedAccessExpression,
    Expression.Parameter(typeof(TModel)));

Expression.Parameter(typeof(TModel) 替换为expression.Parameters[0]

var lambda = Expression.Lambda<Func<TModel, bool>>(
    checkedAccessExpression,
    expression.Parameters[0]));

【讨论】:

  • 太棒了,谢谢!我不知道expression.Paramaters 存在,现在这很有意义。
猜你喜欢
  • 2013-04-07
  • 1970-01-01
  • 1970-01-01
  • 2011-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-06
  • 2016-06-02
相关资源
最近更新 更多