Само по себе дерево выражений не может этого сделать, поскольку у дерева выражений нет возможности узнать, что делает вызов свойства OrgName под прикрытием.
Однако, если вы добавите атрибут к свойствуВозможно, тогда какая-то фабрика могла бы заменить вызов property1 ("OrgName") на путь свойства Organization.Name
Пример кода ниже.
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();
}
}