情景
public class Element {
public int Id {get;set;}
}
public class ViewModel {
public IList<Element> Elements{get;set;}
}
我有一个参数类型为Expression<Func<Element, int>>
的方法,
看起来像m => m.Id
我想改造
m => m.Id
(其中m是元素)
到
x => x.Elements[0].Id
其中x是ViewModel,0是“索引”参数
我现在拥有的(当然是通用的,为清晰起见,我删除了通用部分)
public static class Helpers {
public static Expression<Func<ViewModel, int>> BuildExpressionArrayFromExpression(
this Expression<Func<Element, int>> expression,
ViewModel model,
int index = 0,
string bindingPropertyName = "Elements"//the name of the "List" property in ViewModel class
)
{
var parameter = Expression.Parameter(typeof(ViewModel), "x");
var viewModelProperty = model.GetType().GetProperty(bindingPropertyName);
Expression member = parameter;//x => x
member = Expression.Property(member, viewModelProperty);//x => x.Elements
var test1 = Expression.Property(member, "Item", new Expression[]{Expression.Constant(index)});
//x => x.Elements.Item[0], and I don't want Item
var test2 = Expression.Call(member, viewModelProperty.PropertyType.GetMethod("get_Item"), new Expression[] {Expression.Constant(index)});
//x 0> x.Elements.get_Item(0), and I don't want get_Item(0)
//code to add Id property to expression, not problematic
return Expression.Lambda<Func<ViewModel, int>(member, parameter);
}
}
修改
我需要x => x.Elements[0]
而不是x => x.Elements.Item[0]
,因为
必须使用InputExtensions.TextBoxFor(<myIndexedExpression>)
想象一下这样的课程
public class Test {
public int Id {get;set;}
public IList<Element> Elements {get;set;}
}
和后期行动
[HttpPost]
public ActionResult Edit(Test model) {
bla bla bla.
}
如果输入的名称属性生成不好,那么我就会遇到绑定问题(我的Post Action中的model.Elements为空)。
我输入的名称属性应为
Elements[0]PropertyName
我得到(取决于我的尝试)
PropertyName
或(也许不准确,我试图重现这种情况)
Elements.Item[0].PropertyName
EDIT2
还尝试了一个不同的解决方案,使用ViewData.TemplateInfo.HtmlFieldPrefix, 但我接着
Elements.[0].PropertyName
(和Elements_ 0 _PropertyName为Id)。
第一个点在名称中是不需要的,第一个“双下划线”应该是id中的一个简单。
我实际上使用这个解决方案,使用正则表达式(argh)来删除不需要的东西。和_,但我想避免这种情况。
答案 0 :(得分:6)
这是只是表达式树的字符串表示的问题,你不能改变它。你正在构建的表达式树很好。如果使用lambda表达式构建表达式树,则可以看到相同的效果:
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
class Test
{
public static void Main()
{
Expression<Func<List<string>, string>> expression = list => list[0];
Console.WriteLine(expression);
}
}
输出:
list => list.get_Item(0)
如果在表达式树上调用ToString()
的结果确实你正面临的问题,我会感到非常惊讶。而不是告诉我们你认为你需要的结果和模糊的“我需要它以获得MVC绑定理由”的理由,你应该解释实际上出了什么问题。我强烈怀疑问题不在你想象的地方。
答案 1 :(得分:1)
您应该可以使用MakeIndex来制作索引器表达式:
MemberExpression memberExpr = Expression.Property(member, viewModelProperty);//x => x.Elements
var indexProperty = typeof(IList<Element>).GetProperty("Item");
var indexExpr = Expression.MakeIndex(memberExpr, indexProperty, new Expression[]{Expression.Constant(index)});
return Expression.Lambda<Func<ViewModel, int>(indexExpr, parameter);
答案 2 :(得分:1)
Expression<Func<Element, int>> expr1 =
m => m.Id;
Expression<Func<ViewModel, Element>> expr2 =
x => x.Elements[0];
Expression<Func<ViewModel, int>> result =
expr1.ComposeWith(expr2);
<强>结果:强>
expr1 = m => m.Id
expr2 = x => x.Elements.get_Item(0)
result = x => x.Elements.get_Item(0).Id
它将expr1
(m
)的参数替换为expr2
(x.Elements[0]
)的正文,并将输入参数替换为expr2
的参数(x
)。
扩展方法ComposeWith
:
public static class FunctionalExtensions
{
public static Expression<Func<TInput,TResult>> ComposeWith<TInput,TParam,TResult>(
this Expression<Func<TParam,TResult>> left, Expression<Func<TInput,TParam>> right)
{
var param = left.Parameters.Single();
var visitor = new ParameterReplacementVisitor(p => {
if (p == param)
{
return right.Body;
}
return null;
});
return Expression.Lambda<Func<TInput,TResult>>(
visitor.Visit(left.Body),
right.Parameters.Single());
}
private class ParameterReplacementVisitor : ExpressionVisitor
{
private Func<ParameterExpression, Expression> _replacer;
public ParameterReplacementVisitor(Func<ParameterExpression, Expression> replacer)
{
_replacer = replacer;
}
protected override Expression VisitParameter(ParameterExpression node)
{
var replaced = _replacer(node);
return replaced ?? node;
}
}
}