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
y
which is actually the same inner type of x.List
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();
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>
.