给定一个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> (顺序、分页)。=>好的
因此,我将有一个List<T>为5,10或20 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>),因为网格管理排序和分页,所以我的控制器和服务层不需要知道分页和排序。
更清楚了吗?
发布于 2012-11-19 17:15:50
这可以很容易地通过编写自定义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);发布于 2012-11-19 13:49:46
好吧,有些东西(丑陋的,只是为了测试目的)起作用了,但它让事情变得不清楚,所以我会等待一个更好的解决方案,或者以另一种方式构建我的输入:
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>返回的分页/ordered列表)
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);
...
}https://stackoverflow.com/questions/13452205
复制相似问题