2016-04-19 4 views
2

Как я могу повторить выражение и изменить имена свойств на основе пользовательского атрибута, который я им украсил?Итерации через свойства лямбда-выражения?

Я использую следующий код, чтобы получить пользовательский атрибут свойства, но это работает для простого выражения с одним свойством:

var comparison = predicate.Body as BinaryExpression; 

var member = (comparison.Left.NodeType == ExpressionType.Convert ? 
      ((UnaryExpression)comparison.Left).Operand : 
      comparison.Left) as MemberExpression; 

var value = comparison.Right as ConstantExpression; 

var attribute = Attribute.GetCustomAttribute(member.Member, typeof(MyAttribute)) as MyAttribute; 
var columnName = attribute.Name ?? member.Member.Name; 
var columnValue = value.Value; 

EDIT

Выведение из ExpressionVisitor, я могу изменить имя свойства, переопределяя метод VisitMember.

Это единственное место, где имя свойства используется для построения выражения?

+4

Можете ли вы предоставить некоторые образцы того, что вы пробовали на данный момент? Вы не можете изменить выражение, но вы можете использовать посетителя выражения для создания копии вашей lamba: https://msdn.microsoft.com/en-us/library/bb546136.aspx – Thomas

+2

Вы не можете изменять лямбда-выражения на месте, поэтому вам нужно переписать выражение динамически. Использование [Expression Visitor] (https://msdn.microsoft.com/en-us/library/bb546136 (v = vs.90) .aspx) - хороший подход. – dasblinkenlight

ответ

2

Вы можете реализовать System.Linq.Expressions.ExpressionVisitor, чтобы переписать MemberExpression с новым отображаемым свойством. И да VisitMember - это единственное место, где вы должны реализовать это переназначение, это одно из преимуществ деревьев выражений и посетителей. Единственный странный случай, с которым вам приходится иметь дело, - это тип свойства отличается от типа отображаемого свойства.

void Main() 
{ 
    var data = new List<TestClass>(); 
    data.Add(new TestClass() 
    { 
     FirstName = "First", 
     LastName = "Last", 
    }); 

    var q = data.AsQueryable().Select(x => x.FirstName); 
    var vistor = new MyRewriter(); 

    var newExpression = vistor.Visit(q.Expression); 
    var output = newExpression.ToString(); 
    //System.Collections.Generic.List`1[UserQuery+TestClass].Select(x => x.LastName) 
} 

class TestClass 
{ 
    [MyAttribute(nameof(LastName))] 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
} 

class MyAttribute : Attribute 
{ 
    public string MapTo { get; } 
    public MyAttribute(string mapTo) 
    { 
     MapTo = mapTo; 
    } 
} 


class MyRewriter : ExpressionVisitor 
{ 
    protected override Expression VisitMember(System.Linq.Expressions.MemberExpression node) 
    { 
     var att = node.Member.GetCustomAttribute<MyAttribute>(); 
     if (att != null) 
     { 
      var newMember = node.Expression.Type.GetProperty(att.MapTo); 
      if (newMember != null) 
      { 
       return Expression.Property(
        Visit(node.Expression), // Its very important to remember to visit the inner expression 
        newMember); 
      } 
     } 

     return base.VisitMember(node); 
    } 
} 

Вы можете запустить это в LinqPad для его проверки. Этот код предполагает, что вы сопоставляете свойство.