鉴于Expression<Func<T, TValue>>
(例如m => m.Name
)和index
,我希望能够将表达式转换为m => m[index].Name
)。而且我必须承认我被卡住了......
如果你想要“为什么地狱”(并且可能找到更好的方法),我会告诉你实际情况。
场景:想象一下服务器端可编辑网格(没有javascript )。
我用帮助器构建我的网格,看起来像这样:
@(Html.Grid(Model)
.Columns(columns => {
columns.Edit(m => m.Name);
columns.Edit(m => m.Code);
})
.AsEditable());
模型是IQueryable<T>
m => m.Name
是Expression<Func<T, TValue>>
(TValue
是字符串)
m => m.Code
是Expression<Func<T, TValue>>
(TValue
是int)
渲染我的视图时,我想显示一个html表单。
枚举IQueryable<T>
(顺序,分页)。 =&GT;确定
所以我会有5个,10个或20个List<T>
项T
。
Name
和Code
应使用经典TextBox
表示为HtmlHelper.TextBoxFor(Expression<Func<T, TValue>>)
(创建HtmlHelper<T>
没问题)
但是,由于我有一个列表,如果我想要正确的模型绑定,我不能直接使用m => m.Name
,但应该使用m => m[indexOfItem in List<T>].Name
修改:更多详情:
所以我们假设我们有一个实体类
public class Test {
public int Id {get;set;}
public string Name {get;set;}
public string Code {get;set;}
}
然后,检索IQueryable<Test>
然后是一个视图
@model IQueryable<Test>
@(Html.Grid(Model)
.Columns(columns => {
columns.Edit(m => m.Name);
columns.Edit(m => m.Code);
})
.AsEditable());
如您所见,作为参数给出的模型是IQueryable<Test>
。
m => m.Name
和
m => m.Code
只是模型的属性(我想在我的网格中显示为TextBox)。
该模型是IQueryable<T>
(不是IEnumerable<T>
),因为网格管理排序和分页,因此我的控制器和服务层不需要了解分页和排序。
更清楚吗?
答案 0 :(得分:1)
通过编写自定义ExpressionVisitor:
可以轻松实现这一目标public static class ExpressionExtensions
{
private class Visitor : ExpressionVisitor
{
private readonly int _index;
public Visitor(int index)
{
_index = index;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return Expression.ArrayIndex(GetArrayParameter(node), Expression.Constant(_index));
}
public ParameterExpression GetArrayParameter(ParameterExpression parameter)
{
var arrayType = parameter.Type.MakeArrayType();
return Expression.Parameter(arrayType, parameter.Name);
}
}
public static Expression<Func<T[], TValue>> BuildArrayFromExpression<T, TValue>(
this Expression<Func<T, TValue>> expression,
int index
)
{
var visitor = new Visitor(index);
var nexExpression = visitor.Visit(expression.Body);
var parameter = visitor.GetArrayParameter(expression.Parameters.Single());
return Expression.Lambda<Func<T[], TValue>>(nexExpression, parameter);
}
}
然后您可以像这样使用此扩展方法:
Expression<Func<Test, string>> ex = m => m.Code;
Expression<Func<Test[], string>> newEx = ex.BuildArrayFromExpression(1);
答案 1 :(得分:0)
嗯,得到的东西(丑陋的,仅用于测试目的)起作用,但它使事情不清楚,所以我会等待更好的解决方案或以另一种方式构建我的输入:
public static Expression<Func<T[], TValue>> BuildArrayFromExpression<T, TValue>(this Expression<Func<T, TValue>> expression, int index)
{
var parameter = Expression.Parameter(typeof(T[]), "m");
Expression body = Expression.ArrayIndex(parameter, Expression.Constant(index));
var type = typeof(T);
var properties = expression.Body.ToString().Split('.');//ugly shortcut for test only
foreach (var property in properties.Skip(1))
{
var pi = type.GetProperty(property);
body = Expression.Property(body, type.GetProperty(property));
type = pi.PropertyType;
}
return Expression.Lambda<Func<T[], TValue>>(body, parameter);
}
在RenderMethod中使用,为List<T>
的每一行调用(从IQueryable<T>
返回的分页/有序列表)
public override ... RenderContent(T dataItem) {
var helper = new HtmlHelper<T[]>(GridModel.Context, new GridViewDataContainer<T[]>(GridModel.PaginatedItems.ToArray(), GridModel.Context.ViewData));
var modifiedExpression = Expression.BuildArrayFromExpression(GridModel.PaginatedItems.IndexOf(dataItem));//GridModel.PaginatedItems is the List<T> returned when "executing" the IQueryable<T>
var textBox = helper.TextBoxFor(modifiedExpression);
...
}