Reputation: 16280
How can I iterate through an expression and change the property names based on a custom attribute that I decorated them with?
I use the following code to get the custom attribute of a property, but it works for a simple expression with one property:
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
Deriving from ExpressionVisitor
, I can change the property name by overriding the method VisitMember
.
Is this the only place where a property name is used to build the expression?
Upvotes: 2
Views: 516
Reputation: 2472
You can implement a System.Linq.Expressions.ExpressionVisitor
to rewrite MemberExpression
with the new mapped property. And yes VisitMember
is the only place you have to implement this remapping, this is one of the advantages to expression trees and visitors. The only weird case you have to deal with is if the property's type is different to the type of the mapped property.
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);
}
}
You can run this in LinqPad to test it. This code assumes that you are mapping to a property.
Upvotes: 2