Dynamic expression on List

c# dynamic-linq expression lambda

Question

Given the following classes

public class ClassA
{
    public string StringProperty { get; set; }

    public List<ClassB> List { get; set; }
}

public class ClassB 
{
    public int IntProperty { get; set; }
}

I Would like to dynamically create an expression like the following

x => x.StringProperty == "X" && x.List.Any( y => y.IntProperty > 1 )

No problem for the first part (x.StringProperty == "X"). For the second part I have created a member expression corresponding to x.List and now need to

  1. Create the inner lambda. To do this I need to know the type of y which is actually the same inner type of x.List
  2. Call the Any method on the x.List expression

Any hint on the first point? How do I get the type T of an IEnumerable<T>?

EDIT

I have tried with the following code but it returns null unfortunately

//This expression will be x.List of my original sample 
MemberExpression expr = GetMemberExpression( property, pe );

Type innerType = expr.GetType()
                     .GetInterfaces()
                     .Where( t => t.IsGenericType == true && t.GetGenericTypeDefinition() == typeof( IEnumerable<> ) )
                     .Select( t => t.GetGenericArguments()[0] )
                     .SingleOrDefault();
1
0
5/8/2017 12:25:53 PM

Accepted Answer

Given

private static readonly MethodInfo anyT = (from x in typeof(Enumerable).GetMethods(BindingFlags.Public | BindingFlags.Static)
                                            where x.Name == nameof(Enumerable.Any) && x.IsGenericMethod
                                            let gens = x.GetGenericArguments()
                                            where gens.Length == 1
                                            let pars = x.GetParameters()
                                            where pars.Length == 2 &&
                                                pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(gens[0]) &&
                                                pars[1].ParameterType == typeof(Func<,>).MakeGenericType(gens[0], typeof(bool))
                                            select x).Single();

// https://stackoverflow.com/a/906513/613130
private static IEnumerable<Type> GetGenericIEnumerables(Type type)
{
    return type.GetInterfaces()
                .Where(t => t.IsGenericType == true
                    && t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                .Select(t => t.GetGenericArguments()[0]);
}

Then you can:

var parX = Expression.Parameter(typeof(ClassA), "x");

var list = Expression.Property(parX, nameof(ClassA.List));

var listType = list.Type;
var baseType = GetGenericIEnumerables(listType).First();

var parY = Expression.Parameter(baseType, "y");

var eq = Expression.Equal(
    Expression.Property(parX, nameof(ClassA.StringProperty)),
    Expression.Constant("X"));

var gt = Expression.GreaterThan(
    Expression.Property(parY, "IntProperty"),
    Expression.Constant(1));

var innerExpression = Expression.Lambda(gt, parY);

var any = Expression.Call(
    anyT.MakeGenericMethod(baseType),
    list,
    innerExpression);

var and = Expression.AndAlso(eq, any);

var outerExpression = Expression.Lambda<Func<ClassA, bool>>(and, parX);

var compiled = outerExpression.Compile();

var result = objs.Where(compiled).ToArray();

Note that you don't need to compile the innerExpression: the outerExpression.Compile() will do everything for you!

I'm using a modified version of getting type T from IEnumerable<T> to find the T of a IEnumerable<T>.

1
5/23/2017 12:34:30 PM


Related Questions





Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow