Как расширить (встроенное) свойство в выражении (C # 4.0)? - PullRequest
1 голос
/ 18 февраля 2011

У меня есть выражение этой формы:

Expression<Func<ShowParticipant, bool>> expr = z => z.Show.OrgName == "xyz";

Мне нужно преобразовать / развернуть его в следующую форму:

Expression<Func<ShowParticipant, bool>> expr = z => z.Show.Organization.Name == "xyz";

где OrgName свойство для Show объекта разрешается в Organization.Name. Как я могу достичь этого, предполагая, что мне это нужно для работы в EF? Вы можете представить OrgName, определенный в классе Показать, как показано ниже -

public partial class Show
{
    public string OrgName
    {
        get
        {
            return this.Organization.Name;
        }
        set
        {
            this.Organization.Name = value;
        }
    }
}

Ценю ваш ответ,

Ананд.

Ответы [ 2 ]

1 голос
/ 23 февраля 2011

Само по себе дерево выражений не может этого сделать, поскольку у дерева выражений нет возможности узнать, что делает вызов свойства 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();
        }
    }
0 голосов
/ 25 февраля 2011

Спасибо за ваш ответ. Это точно отвечает на мой вопрос. В то же время я придумал решение с использованием ExpressionVisitor. Это не полный, но я даю подход, который я взял здесь -

public class ExpressionExpander : ExpressionVisitor
{
    private Dictionary<Expression, Expression> parameterMap = new Dictionary<Expression, Expression>();

    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (parameterMap.ContainsKey(node))
            return parameterMap[node];

        return node;
    }

    protected override Expression VisitMember(MemberExpression node)
    {
            var newObj = Visit(node.Expression);
            if (NeedsInlineExpansion(newObj.Type, node.Member.Name))
            {
                LambdaExpression exp = GetPropertyTransform();

                if (parameterMap.ContainsKey(node.Expression))
                    parameterMap.Add(exp.Parameters[0], node.Expression);

                var visitedExp = Visit(exp.Body);
                var memExp = (MemberExpression)visitedExp;

                parameterMap.Add(node, memExp);
                return memExp;
            }
            else
            {
                var newMember = newObj.Type.GetMember(node.Member.Name).First();
                var newMemberAccessExpr = Expression.MakeMemberAccess(newObj, newMember);
                parameterMap.Add(node, newMemberAccessExpr);
                return newMemberAccessExpr;
            }
    }

    private bool NeedsInlineExpansion(Type type, string coreMemberName)
    {
        // Figure out way to determine if the property needs to be flattened out...
        // may be using attribute on Property
    }

    private LambdaExpression GetPropertyTransform()
    {
        // this is hardcoded right now, but represents some mechanism of getting a lambda 
        // returned for property in question...
        Expression<Func<Show, string>> exp = z => z.Organization.Name;
        var lambda = (LambdaExpression)exp;
        return lambda;
    }
}

А на звонилке будет выглядеть так -

        Expression<Func<ShowParticipant, bool>> expr1 = z => z.Show.OrgName == "xyz";
        ExpressionExpander expander = new ExpressionExpander();
        var inlineExpanded = expander.Visit(expr1);

Спасибо, Ананд.

...