关于c#:动态地将IEnumerable转换为IQueryable或使用LINQ Expressions动态调用AsQueryable

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

我正在动态地创建LINQ查询,到目前为止我做得还不错。

但我被困在了我想不到的地方。在构建该查询的某个时刻,我需要访问实体的enityCollection。像这样:

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

然后,我将在该集合上调用"Where"Linq方法:

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

通常情况下,这是可行的。在这种情况下,不会这样做,因为集合是IEnumerable的,我需要它是iQuery,以便"在哪里"工作。

我试过这个:

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

但它表示无法强制转换,因为EntityCollection不实现iQuery。

我静态地使用AsQueryable来实现我在这里所需要的,所以我尝试动态地模拟:

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

但我得到空引用异常。我无法通过反射来达到它。这个asqueryable方法是扩展方法,它是静态的,在queryable类中定义的,所以我尝试了:

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

结果相同:"值不能为空"。

我在这里达到了我的极限,而且我刚刚摆脱了想法。

所以,我要问你:

如何将IEnumerable动态转换为iqueryable?


试着这样得到方法:

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

然后,您应该能够像这样构造对该方法的调用:

1
Expression.Call(method, collection);

您的代码的问题是绑定标志很难使用。如果指定了任何绑定标志(如bindingFlags.static),则还必须显式地指定是要bindingFlags.public还是bindingFlags.nonpublic。

第二个问题是有两个可查询的方法——通用方法和非通用方法。提供类型参数数组可以解决这种歧义。


"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."

不,你没有。对于可枚举的,使用Enumerable.Where而不是Queryable.Where

1
2
3
4
5
6
7
8
var query =
    from customer in Context.Customers
    where customer.Id == YourCustomerId // 1
    select new
    {
        Customer = customer,
        OrderCount = customer.Orders.Where(order => order.IsOpen).Count() // 2
    };

第一个"where"决定为Queryable.Where,而第二个"where"决定为Enumerable.Where。这不是问题,也不是效率低下,因为整个表达式是子查询的一部分,所以它仍将被发送到查询提供程序并(f.e.)转换为SQL。


好吧,我想我明白了:

首先,通过反射得到方法,如igor所说:

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

然后,我使用了不同版本的表达式。调用以克服静态/实例不匹配:

1
Expression buff = Expression.Call(mi, new[] { collection });

最后将其转换为类型化的asqueryable:

1
Expression final = Expression.Convert(buff, typeof(IQueryable<RelatedEntity>));