1

设想

public class Element {
   public int Id {get;set;}
}

public class ViewModel {
   public IList<Element> Elements{get;set;}
}

我有一个带有 type 参数的方法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.
}

如果我的输入的名称属性没有很好地生成,那么我就会遇到绑定问题(model.Elements 在我的 Post Action 中是空的)。

我输入的名称属性应该是

Elements[0]PropertyName

我得到(取决于我的尝试)

PropertyName

或(也许不准确,我尝试重现这种情况)

Elements.Item[0].PropertyName

编辑2

还尝试了一个不同的解决方案,使用 ViewData.TemplateInfo.HtmlFieldPrefix,但我得到了

Elements.[0].PropertyName

(和 Elements_ 0 _PropertyName 作为 Id)。

第一个点在名称中是不需要的,第一个“双下划线”应该是 id 中的简单点。

我实际上使用这个解决方案,使用正则表达式(argh)来删除不需要的。和 _ ,但我想避免这种情况。

4

3 回答 3

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 绑定原因”的理由,不如说明实际出了什么问题。我强烈怀疑问题不在你认为的地方。

于 2013-04-24T17:34:26.330 回答
2
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]expr2x

扩展方法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;
        }
    }
}
于 2013-04-29T13:37:37.777 回答
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);
于 2013-03-13T18:58:16.800 回答