【发布时间】: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 => x.Options 以在正确的数组索引处访问对象的正确属性,但是这给了我一个带有消息的模板错误
模板只能用于字段访问、属性访问、一维数组索引或单参数自定义索引器表达式。
据我所知,我只做一个单 D 数组索引,然后是属性访问,所以我不确定我为什么会看到这个。我之前尝试过var cbx = html.CheckBoxFor(lambda);,但这不起作用,因为参数x 仅在视图范围内定义。
假设我想要的甚至是可能的,任何人都可以帮助实现它吗?我不熟悉以这种方式操作Expressions。
【问题讨论】:
-
不清楚您想要传递给该方法的内容 - 您当前在两个参数中传递相同的模型。我无论如何,为什么不使用
EditorTempate和简单的@Html.EditorFor(m => m.Options)(错误是因为x => lambda.Compile()(x)这没有任何意义,但又一次,不知道你想用它来实现什么 -
你可以用一半的代码来做这一切,如果你总是传递一个
IOptionList的数组,签名应该只是public static CheckBoxFormGroupFor<TModel,>(this HtmlHelper<TModel> html, Expression<Func<TModel, IEnumerable<IOptionList>>> expression) -
是的,我考虑过使用
IEnumerable<IOptionList>,但是为了索引列表,我需要调用ToArray(Expression.ArrayIndexonly 适用于数组)和模板对于 MVC 的 *For 方法不能包含方法调用 - 它会破坏模型绑定。 -
您可以使其正常工作(与使用 ModelMetadata 的内置 HtmlHelper 方法的工作方式相同)。但是仍然不清楚为什么你不只为
Option类型创建一个EditorTemplate(只包含@Html.CheckBoxFor(x => x.Checked)和@Html.LabelFor(x => x.Checked, Model.Name))而只使用@Html.EditorFor(m => m.Options)
标签: c# asp.net-mvc lambda linq-expressions