1

シナリオ

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.
}

入力の名前属性が適切に生成されていない場合は、バインディングの問題が発生します(モデル。要素はポストアクションで空です)。

入力の名前属性は次のようになります

Elements[0]PropertyName

そして私は(私の試みに応じて)得る

PropertyName

または(正確ではないかもしれませんが、私はこのケースを再現しようとしています)

Elements.Item[0].PropertyName

EDIT2

また、ViewData.TemplateInfo.HtmlFieldPrefixを使用して別の解決策を試しましたが、その後、

Elements.[0].PropertyName

(およびElements_ 0 _PropertyName as Id)。

最初のドットは名前では不要であり、最初の「二重アンダースコア」はidの単純なものである必要があります。

私は実際にこのソリューションを使用し、正規表現(argh)を使用して不要なものを削除します。と_、しかし私はこれを避けたいです。

4

3 に答える 3

6

これは式ツリーの文字列表現の問題であり、変更することはできません作成している式ツリーは問題ありません。ラムダ式を使用して式ツリーを構築した場合も、同じ効果が見られます。

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

( )のパラメーターをexpr1m)の本体に置き換え、入力パラメーターを()からのパラメーターに置き換え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 に答える