Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically cast IEnumerable to IQueryable or dynamically call AsQueryable with LINQ Expressions

Tags:

c#

dynamic

linq

I am creating LINQ query dynamically, and I am doing OK so far.

But I am stuck where I thought I wouldn't be. At a certain point in building that query, I need to access EnityCollection of an entity. Something like this:

Expression collection = Expression.Property(entity, typeof(EntityType).GetProperty("CollectionOfRelatedEntities"));

Then, I would call "Where" LINQ method upon that collection:

MethodCallExpression AfterWhere = Expression.Call(
                        typeof(Queryable),
                        "Where",
                        new Type[] { typeof(RelatedEntity) },
                        collection,
                        Expression.Lambda<Func<RelatedEntity, bool>>(predicate, new ParameterExpression[] { paramOfRelatedEntity }));

And normally that would work. In this case it won't because collection is IEnumerable and I need it to be IQueryable in order "Where" to work.

I tried this:

Expression.Convert(collection, typeof(IQueryable<RelatedEntity>);

but it says unable to cast because EntityCollection doesn't implement IQueryable.

I statically use AsQueryable to achieve what i need here, so i tried mimicking that dynamically:

Expression.Call(collection, typeof(EntityCollection<RelatedEntity>).GetMethod("AsQueryable"));

but I get null reference exception. I can't reach it via reflection. This AsQueryable method is extension method, it is static, defined in Queryable class, so i tried:

Expression.Call(collection, typeof(Queryable).GetMethod("AsQueryable", BindingFlags.Static)); 

Same result: "Value cannot be null".

I am reaching my limits here, and I am fresh out of ideas.

So, I am asking you:

How can I dynamically cast IEnumerable to IQueryable?

like image 583
Milos Mijatovic Avatar asked Feb 17 '12 23:02

Milos Mijatovic


1 Answers

Try to get the method this way:

var method = typeof(Queryable).GetMethod(
    "AsQueryable",
    BindingFlags.Static | BindingFlags.Public, 
    null, 
    new [] { typeof(IEnumerable<RelatedEntity>)}, 
    null);

Then, you should be able to construct a call to that method like this:

Expression.Call(method, collection);

The problem with your code was that BindingFlags are tricky to use. If you specify any BindingFlags - like BindingFlags.Static - then you also have to explicitly say whether you want BindingFlags.Public or BindingFlags.NonPublic.

Then the second problem is that there are two AsQueryable methods - a generic one and a non-generic one. Providing the array of type arguments resolves that ambiguity.

like image 106
Igor ostrovsky Avatar answered Sep 28 '22 06:09

Igor ostrovsky