vendredi 17 août 2018

Dynamic expression tree with method 'Select'

I'm trying to build the following lambda expression using the expression tree ->

info => info.event_objects.Select(x => x.object_info.contact_info)

I researched a lot and find some answers on the StackOverflow. This one helped me to build the

info => 
     info.event_objects.Any(x => x.object_info.contact_info.someBool == true)

As you can see, the method 'Any' is easy to get.

var anyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" 
&& m.GetParameters().Length == 2);
anyMethod = anyMethod.MakeGenericMethod(childType);

The main problem is with the method 'Select'. If you will try to change the Name "Any" to "Select", you will get the following exception:

var selectMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == 
"Select" && m.GetParameters().Length == 2);
selectMethod = selectMethod.MakeGenericMethod(childType);

Additional information: Sequence contains more than one matching element

Another way I've tried:

MethodInfo selectMethod = null;
foreach (MethodInfo m in typeof(Enumerable).GetMethods().Where(m => m.Name 
  == "Select"))
    foreach (ParameterInfo p in m.GetParameters().Where(p => 
           p.Name.Equals("selector")))
        if (p.ParameterType.GetGenericArguments().Count() == 2)
            selectMethod = (MethodInfo)p.Member;

It seems work, but then I get the exception here:

navigationPropertyPredicate = Expression.Call(selectMethod, parameter, 
navigationPropertyPredicate);

 Additional information: Method 
 System.Collections.Generic.IEnumerable`1[TResult] Select[TSource,TResult] 
 (System.Collections.Generic.IEnumerable`1[TSource], 
 System.Func`2[TSource,TResult]) is a generic method definition> 

After that, I've tried to use:

selectMethod = selectMethod.MakeGenericMethod(typeof(event_objects), 
typeof(contact_info));

In fact, it doesn't help.

Here is my full code

 public static Expression GetNavigationPropertyExpression(Expression parameter, params string[] properties)
    {
        Expression resultExpression = null;
        Expression childParameter, navigationPropertyPredicate;
        Type childType = null;

        if (properties.Count() > 1)
        {
            //build path
            parameter = Expression.Property(parameter, properties[0]);
            var isCollection = typeof(IEnumerable).IsAssignableFrom(parameter.Type);
            //if it´s a collection we later need to use the predicate in the methodexpressioncall
            if (isCollection)
            {
                childType = parameter.Type.GetGenericArguments()[0];
                childParameter = Expression.Parameter(childType, "x");
            }
            else
            {
                childParameter = parameter;
            }
            //skip current property and get navigation property expression recursivly
            var innerProperties = properties.Skip(1).ToArray();
            navigationPropertyPredicate = GetNavigationPropertyExpression(childParameter, innerProperties);
            if (isCollection)
            {
                //var selectMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Select" && m.GetParameters().Length == 2);
                //selectMethod = selectMethod.MakeGenericMethod(childType);
                MethodInfo selectMethod = null;
                foreach (MethodInfo m in typeof(Enumerable).GetMethods().Where(m => m.Name == "Select"))
                    foreach (ParameterInfo p in m.GetParameters().Where(p => p.Name.Equals("selector")))
                        if (p.ParameterType.GetGenericArguments().Count() == 2)
                            selectMethod = (MethodInfo)p.Member;

                navigationPropertyPredicate = Expression.Call(selectMethod, parameter, navigationPropertyPredicate);
                resultExpression = MakeLambda(parameter, navigationPropertyPredicate);
            }
            else
            {
                resultExpression = navigationPropertyPredicate;
            }
        }
        else
        {
            var childProperty = parameter.Type.GetProperty(properties[0]);
            var left = Expression.Property(parameter, childProperty);
            var right = Expression.Constant(true, typeof(bool));
            navigationPropertyPredicate = Expression.Lambda(left);
            resultExpression = MakeLambda(parameter, navigationPropertyPredicate);
        }
        return resultExpression;
    }


    private static Expression MakeLambda(Expression parameter, Expression predicate)
    {
        var resultParameterVisitor = new ParameterVisitor();
        resultParameterVisitor.Visit(parameter);
        var resultParameter = resultParameterVisitor.Parameter;
        return Expression.Lambda(predicate, (ParameterExpression)resultParameter);
    }

    private class ParameterVisitor : ExpressionVisitor
    {
        public Expression Parameter
        {
            get;
            private set;
        }
        protected override Expression VisitParameter(ParameterExpression node)
        {
            Parameter = node;
            return node;
        }
    }


    [TestMethod]
    public void TestDynamicExpression()
    {
        var parameter = Expression.Parameter(typeof(event_info), "x");
        var expression = GetNavigationPropertyExpression(parameter, "event_objects", "object_info", "contact_info");
    }





Aucun commentaire:

Enregistrer un commentaire