By itself, expression trees can't do this as there's no way for an expression tree to know what calling the OrgName property does under the covers.
However if you were to put an attribute on the property, perhaps then some factory could replace a call to property1 ("OrgName") with the property path Organization.Name
Sample code below.
static void Main(string[] args)
{
Company company = new Company { Organization = { Name = "Microsoft" } };
Expression<Func<Company, int>> lambdaExpression = c => c.OrgName.Length;
var expanded = Expand(lambdaExpression);
}
private static Expression<Func<TSource, TResult>> Expand<TSource, TResult>(Expression<Func<TSource, TResult>> lambdaExpression)
{
Expression expanded = GetExpandedExpression(lambdaExpression.Body);
if (Object.ReferenceEquals(lambdaExpression.Body, expanded))
{
return lambdaExpression;
}
return Expression.Lambda<Func<TSource, TResult>>(
expanded,
lambdaExpression.Parameters
);
}
private static Expression GetExpandedExpression(Expression expression)
{
Expression expandedContainer;
switch (expression.NodeType)
{
case ExpressionType.MemberAccess:
MemberExpression memberExpression = (MemberExpression)expression;
if (memberExpression.Expression != null)
{
expandedContainer = GetExpandedExpression(memberExpression.Expression);
PropertyPathAttribute attribute = memberExpression.Member.GetCustomAttributes(typeof(PropertyPathAttribute), false).Cast<PropertyPathAttribute>().FirstOrDefault();
if (attribute != null && !String.IsNullOrEmpty(attribute.Path))
{
string[] parts = attribute.Path.Split('.');
expression = expandedContainer;
for (int i = 0; i < parts.Length; i++)
{
string part = parts[i];
expression = Expression.MakeMemberAccess(
expression,
(MemberInfo)expression.Type.GetProperty(part, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ??
expression.Type.GetField(part, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
);
}
}
else if (!Object.ReferenceEquals(expandedContainer, memberExpression.Expression))
{
return Expression.MakeMemberAccess(
expandedContainer,
memberExpression.Member
);
}
}
break;
case ExpressionType.ArrayLength:
UnaryExpression unaryExpression = (UnaryExpression)expression;
if (!Object.ReferenceEquals(expandedContainer = GetExpandedExpression(unaryExpression.Operand), unaryExpression.Operand))
{
return Expression.ArrayLength(expandedContainer);
}
break;
}
return expression;
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public sealed class PropertyPathAttribute : Attribute
{
private readonly string _path;
public string Path
{
get
{
return this._path;
}
}
public PropertyPathAttribute(string path)
{
this._path = path;
}
}
public class Organization
{
public string Name { get; set; }
}
public class Company
{
[PropertyPath("Organization.Name")]
public string OrgName
{
get
{
return this.Organization.Name;
}
set
{
this.Organization.Name = value;
}
}
public Organization Organization { get; private set; }
public Company()
{
this.Organization = new Organization();
}
}