将Lambda表达式转换为表达式树

时间:2014-06-11 02:17:37

标签: linq reflection lambda tree expression

我有那个lambda:

var Ids = profileExample.CostCenters
           .Where(CostCentre => CostCentre != null)
           .Select(CostCentre => CostCentre.Id);

然后我转换为该表达式树

    static IEnumerable<Int64> AboveLambdaConvertedToExpressionTree(Profile profileExample)
    {
        //Begin     var Ids = profileExample.CostCenters.Where(CostCentre => CostCentre != null).Select(CostCentre => CostCentre.Id);
        var property = profileExample.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.Name != "Id").First();
        var collection = ((IEnumerable)property.GetValue(profileExample, null)).AsQueryable();
        var collectionType = property.PropertyType.GetGenericArguments()[0];
        var collectionTypeName = collectionType.Name;

        var keyType = typeof(Int64);
        var keyName = "Id";

        //BeginWhere
        var parameter = Expression.Parameter(collectionType, collectionTypeName);

        var profileExampleWhere = Expression.Lambda(
                                            Expression.NotEqual(parameter, Expression.Constant(null)),
                                            parameter);

        var profileExampleWhereCall = Expression.Call(typeof(Enumerable),
                                                                "Where",
                                                                new Type[] { collectionType },
                                                                collection.Expression,
                                                                profileExampleWhere);
        //EndWhere

        //BeginSelect
        var profileExampleSelect = Expression.Lambda(Expression.PropertyOrField(parameter, keyName),
                                         parameter);

        var profileExampleSelectCall = Expression.Call(typeof(Enumerable),
                                                  "Select",
                                                  new Type[] { collectionType, keyType },
                                                  profileExampleWhereCall,
                                                  profileExampleSelect);



        var Ids = Expression.Lambda(profileExampleSelectCall).Compile().DynamicInvoke();
        //EndSelect
        //End     var Ids = profileExample.CostCenters.Where(CostCentre => CostCentre != null).Select(CostCentre => CostCentre.Id);

        return ((IEnumerable)Ids).Cast<Int64>();
    }

现在我想对下面的lambda

做同样的事情
var result = Set.AsQueryable()
               .Where(Profile => Profile.CostCenters.Select(CostCentre => CostCentre.Id)
               .Any(Id => Ids.Contains(Id))).ToList();

但我坚持.Any(Id => Ids.Contains(Id))....

        var id = Expression.Parameter(typeof(long), "Id");
        var costCentre = Expression.Parameter(typeof(CostCentre), "CostCentre");
        var profile = Expression.Parameter(typeof(Profile), "Profile");
        var selectLambda = Expression.Lambda(Expression.PropertyOrField(costCentre, "Id"), costCentre);
        var selectCall = Expression.Call(typeof(Enumerable),
                                 "Select",
                                 new Type[] { typeof(CostCentre), typeof(long) }, 
                                 Expression.PropertyOrField(profile, "CostCenters"),
                                 selectLambda);  

如何从selectCall调用Any并调用Ids.Contains ...


作为控制台应用程序运行的完整代码:


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace ExpressionTrees
{
    class Program
    {
        static void Main(string[] args)
        {
            var Ids = profileExample.CostCenters.Where(CostCentre => CostCentre != null).Select(CostCentre => CostCentre.Id);

            Ids = AboveLambdaConvertedToExpressionTree(profileExample);

            var result = Set.AsQueryable().Where(Profile => Profile.CostCenters.Select(CostCentre => CostCentre.Id).Any(Id => Ids.Contains(Id))).ToList();

            //Expression<Func<Profile, bool>> lambda = (Profile) => Profile.CostCenters.Select(CostCentre => CostCentre.Id).Any(Id => Ids.Contains(Id));

            var id = Expression.Parameter(typeof(long), "Id");
            var costCentre = Expression.Parameter(typeof(CostCentre), "CostCentre");
            var profile = Expression.Parameter(typeof(Profile), "Profile");
            var selectLambda = Expression.Lambda(Expression.PropertyOrField(costCentre, "Id"), costCentre);
            var selectCall = Expression.Call(typeof(Enumerable),
                                     "Select",
                                     new Type[] { typeof(CostCentre), typeof(long) }, 
                                     Expression.PropertyOrField(profile, "CostCenters"),
                                     selectLambda);                                   
        }

        static IEnumerable<Int64> AboveLambdaConvertedToExpressionTree(Profile profileExample)
        {
            // I show that as example of what i need to do
            var keyType = typeof(Int64);
            var keyName = "Id";

            //Begin     var Ids = profileExample.CostCenters.Where(CostCentre => CostCentre != null).Select(CostCentre => CostCentre.Id);
            var property = profileExample.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.Name != keyName).First();
            var collection = ((IEnumerable)property.GetValue(profileExample, null)).AsQueryable();
            var collectionType = property.PropertyType.GetGenericArguments()[0];
            var collectionTypeName = collectionType.Name;

            //BeginWhere
            var parameter = Expression.Parameter(collectionType, collectionTypeName);

            var profileExampleWhere = Expression.Lambda(
                                                Expression.NotEqual(parameter, Expression.Constant(null)),
                                                parameter);

            var profileExampleWhereCall = Expression.Call(typeof(Enumerable),
                                                                    "Where",
                                                                    new Type[] { collectionType },
                                                                    collection.Expression,
                                                                    profileExampleWhere);
            //EndWhere

            //BeginSelect
            var profileExampleSelect = Expression.Lambda(Expression.PropertyOrField(parameter, keyName),
                                             parameter);

            var profileExampleSelectCall = Expression.Call(typeof(Enumerable),
                                                      "Select",
                                                      new Type[] { collectionType, keyType },
                                                      profileExampleWhereCall,
                                                      profileExampleSelect);



            var Ids = Expression.Lambda(profileExampleSelectCall).Compile().DynamicInvoke();
            //EndSelect
            //End     var Ids = profileExample.CostCenters.Where(CostCentre => CostCentre != null).Select(CostCentre => CostCentre.Id);

            return ((IEnumerable)Ids).Cast<Int64>();
        }

        public partial class Profile
        {
            public virtual Int64 Id { get; set; }

            public virtual ICollection<CostCentre> CostCenters { get; set; }
        }


        public partial class CostCentre
        {
            public virtual Int64 Id { get; set; }
        }
        public static Profile profileExample
        {
            get
            {
                return new Profile()
                {
                    Id = 1,
                    CostCenters = new List<CostCentre>() { new CostCentre() { Id = 2 } }
                };
            }
        }

        public static IList<Profile> Set
        {
            get
            {
                return new List<Profile>() { new Profile() { Id = 1, 
                                                            CostCenters = new List<CostCentre>() {  new CostCentre() { Id = 1 }, 
                                                                                                    new CostCentre() { Id = 2 } } 
                                                          },            
                                            new Profile() { Id = 2, 
                                                            CostCenters = new List<CostCentre>() {  new CostCentre() { Id = 2 }, 
                                                                                                    new CostCentre() { Id = 3 } } 
                                            },
                                            new Profile() { Id = 3, 
                                                            CostCenters = new List<CostCentre>() {  new CostCentre() { Id = 3 } } 
                                            } };
            }
        }
    }
}

2 个答案:

答案 0 :(得分:0)

由于Any是Generic Method,您需要为特定类型创建它。以下方法从Any<T>类型获取Enumerable方法。

public static MethodInfo GetAnyExtensionMethod(Type forType)
{
    MethodInfo method = 
        typeof(Enumerable).GetMethods()
                          .First(m => m.Name.Equals("Any") && 
                                      m.GetParameters().Count() == 2);
    return method.MakeGenericMethod(new[] { forType });
}

答案 1 :(得分:0)

它在MS的Mads的帮助下解决了

class Program
{
    static void Main(string[] args)
    {
        //var Ids = profileExample.CostCenters.Where(CostCentre => CostCentre != null).Select(CostCentre => CostCentre.Id);

        var Ids = AboveLambdaConvertedToExpressionTree(profileExample);

        //var result = Set.AsQueryable().Where(Profile => Profile.CostCenters.Select(CostCentre => CostCentre.Id).Any(Id => Ids.Contains(Id))).ToList();

        var id = Expression.Parameter(typeof(long), "Id");
        var costCentre = Expression.Parameter(typeof(CostCentre), "CostCentre");
        var profile = Expression.Parameter(typeof(Profile), "Profile");
        var selectLambda = Expression.Lambda(Expression.PropertyOrField(costCentre, "Id"), costCentre);
        var selectCall = Expression.Call(typeof(Enumerable),
                                 "Select",
                                 new Type[] { typeof(CostCentre), typeof(long) }, 
                                 Expression.PropertyOrField(profile, "CostCenters"),
                                 selectLambda);                      

        //var id2 = Expression.Parameter(typeof(long), "Id");

        var containsCall = Expression.Call(typeof(Enumerable),
                              "Contains",
                              new Type[] { typeof(long) },
                              Expression.Constant(Ids),
                              id);

        var anyLambda = Expression.Lambda(containsCall, id);

        var anyCall = Expression.Call(typeof(Enumerable),
                     "Any",
                     new Type[] { typeof(long) },
                     selectCall,
                     anyLambda);

        var whereLambda = Expression.Lambda(anyCall, profile);

        var callExpression = Expression.Call(typeof(Queryable),
                                                    "Where",
                                                    new Type[] { typeof(Profile) },
                                                    Set.AsQueryable().Expression,
                                                    whereLambda);


        var result = Expression.Lambda(callExpression).Compile().DynamicInvoke();


    }