C#: Getting Names of Properties in a Chain from Lambda Expression

C#: Getting Names of properties in a chain from lambda expression

Something like this?

public void Foo<T, P>(Expression<Func<T, P>> expr)
{
MemberExpression me;
switch (expr.Body.NodeType)
{
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
var ue = expr.Body as UnaryExpression;
me = ((ue != null) ? ue.Operand : null) as MemberExpression;
break;
default:
me = expr.Body as MemberExpression;
break;
}

while (me != null)
{
string propertyName = me.Member.Name;
Type propertyType = me.Type;

Console.WriteLine(propertyName + ": " + propertyType);

me = me.Expression as MemberExpression;
}
}

Getting Names of properties in a chain from NewExpression members

Maybe you can try on Arguments instead of Members.

var body = exp.Body as NewExpression;
foreach (var member in body.Arguments)
{
Console.WriteLine(member);
}

Output is:

dt.Second
dt.Hour
dt.Date.Day

Get the name of the lambda expression with the index from the expression's array

The indexer is translated to a MethodCallExpression, so you need to override VisitMethodCall.

You can do a visitor like this:

public class PathVisitor : ExpressionVisitor
{

// You should not use MemberInfo for the list type anymore, since the path can have indexer expressions now!
// Here I convert both the indexer expressions and the member accesses to string in the visitor
// You can do something more fancy, by creating your own class that represents either of these two cases
// and then formatting them afterwards.
internal readonly List<string> Path = new List<string>();

protected override Expression VisitMember(MemberExpression node)
{
if (node.Member is PropertyInfo)
{
Path.Add(node.Member.Name);
}
return base.VisitMember(node);
}

protected override Expression VisitMethodCall(MethodCallExpression node) {
if (node.Method.Name == "get_Item") { // name of the indexer

// you can format this in a better way on your own. This is just an example
Path.Add(node.Arguments.First().ToString());
}
return base.VisitMethodCall(node);
}
}

Usage:

public static IReadOnlyList<string> Get(LambdaExpression expression)
{
var visitor = new PathVisitor();
visitor.Visit(expression.Body);
visitor.Path.Reverse();
return visitor.Path;
}

public static string PathString(Expression<Func<object>> expression)
{
return string.Join(".", Get(expression));
}

This produces an output of Adresses.2.Number for your lambda.

Note that this assumes the lambdas are only with property accesses and indexers with constant arguments.

Get chained property names from MemberExpression

You need a little recursion:

private static string GetPropertyPath<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression)
{
var propertyPath = new Stack<string>();
var body = (MemberExpression)expression.Body;

do
{
propertyPath.Push(body.Member.Name);

// this will evaluate to null when we will reach ParameterExpression (x in "x => x.Foo.Bar....")
body = body.Expression as MemberExpression;
}
while (body != null);

return string.Join(".", propertyPath);
}

Retrieving Property name from lambda expression

I found another way you can do it was to have the source and property strongly typed and explicitly infer the input for the lambda. Not sure if that is correct terminology but here is the result.

public static RouteValueDictionary GetInfo<T,P>(this HtmlHelper html, Expression<Func<T, P>> action) where T : class
{
var expression = (MemberExpression)action.Body;
string name = expression.Member.Name;

return GetInfo(html, name);
}

And then call it like so.

GetInfo((User u) => u.UserId);

and voila it works.

How to get the full name of the property from a lambda

Or you can use

expression.Compile().Invoke(obj)

if you want to use Expression<>

Write a lambda expression to get a list of properties from a list of objects

If ProjectDetails.primitives is a single ProjectPrimitives, you need Select() to transform the list from ProjectDetails to ProjectPrimitives:

var prims = myList.Select(i => i.primitives).ToList();

If it is a collection, such as List<ProjectPrimitives>, you need SelectMany():

var prims = myList.SelectMany(i => i.primitives).ToList();

Pass Lambda to Chain Over Object Members to Function

You can create your own expression visitor that will be able to keep track of all member accesses where the member is being accessed from a parameter.

internal class ParameterAccessesVisitor : ExpressionVisitor
{
private ReadOnlyCollection<ParameterExpression> parameters;
public ParameterAccessesVisitor(
ReadOnlyCollection<ParameterExpression> parameters)
{
VisitedMembers = new List<MemberInfo>();
this.parameters = parameters;
}
protected override Expression VisitMember(MemberExpression node)
{
if (parameters.Contains(node.Expression))
VisitedMembers.Add(node.Member);
return base.VisitMember(node);
}
public List<MemberInfo> VisitedMembers { get; private set; }
}

We can then create a method to use this visitor that accepts an expression (which should be a lambda expression), pulls out the parameters, find all member accesses to those parameters, and then returns the sequence:

public static IEnumerable<MemberInfo> ParameterAccesses<T, TResult>(
Expression<Func<T, TResult>> lambda)
{
var visitor = new ParameterAccessesVisitor(lambda.Parameters);
visitor.Visit(lambda);
return visitor.VisitedMembers;
}

If you want just the member names, rather than the full MemberInfo, then a simple Select can select out the names into an array.

We can now write:

var members = ParameterAccesses((List<int> p) => new { p.Count, p.Capacity })
.Select(member => member.Name);

And have it print out:

Count

Capacity

Since you want to grab a value of a given attribute from these types, you can easily add another method to perform such a query based on the above method:

public static IEnumerable<string> GetLogicalNames<T, TResult>(
Expression<Func<T, TResult>> lambda)
{
return from member in ParameterAccesses(lambda)
let attributes = member.GetCustomAttributes<AttributeLogicalNameAttribute>()
where attributes.Any()
select attributes.First().LogicalName;
}


Related Topics



Leave a reply



Submit