Динамически приводить IEnumerable к IQueryable или динамически вызывать AsQueryable с помощью выражений LINQ. - PullRequest
8 голосов
/ 18 февраля 2012

Я создаю запрос LINQ динамически, и пока все в порядке.

Но я застрял там, где думал, что не буду.В определенный момент при создании этого запроса мне нужно получить доступ к EnityCollection объекта.Примерно так:

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

Затем я бы вызвал метод LINQ "Где" для этой коллекции:

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

И обычно это будет работать.В этом случае это не так, потому что коллекция IEnumerable, и мне нужно, чтобы она была IQueryable для того, чтобы «Где» работать.

Я пробовал это:

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

, но он говорит, что не можетприведение, потому что EntityCollection не реализует IQueryable.

Я статически использую AsQueryable для достижения того, что мне нужно, поэтому я попытался имитировать это динамически:

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

, но я получаю исключение нулевой ссылки.Я не могу достичь этого через отражение.Этот метод AsQueryable является методом расширения, он является статическим, определенным в классе Queryable, поэтому я попытался:

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

Тот же результат: «Значение не может быть нулевым».

Я достигаю своих пределовздесь, и я только что из идей.

Итак, я спрашиваю вас:

Как я могу динамически приводить IEnumerable к IQueryable?

Ответы [ 3 ]

5 голосов
/ 18 февраля 2012

Попробуйте получить метод следующим образом:

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

Тогда вы сможете создать вызов этого метода следующим образом:

Expression.Call(method, collection);

Проблема с вашим кодом заключалась в том, что BindingFlags сложно использовать. Если вы укажете какие-либо BindingFlags - например, BindingFlags.Static - тогда вы также должны явно указать, хотите ли вы BindingFlags.Public или BindingFlags.NonPublic.

Тогда вторая проблема заключается в том, что есть два метода AsQueryable - общий и неуниверсальный. Предоставление массива аргументов типа решает эту неоднозначность.

2 голосов
/ 18 февраля 2012

"И обычно это будет работать. В этом случае это не будет, потому что коллекция IEnumerable, и мне нужно, чтобы она была IQueryable для" Где "работать."

Нет, нет. Для перечисления используйте Enumerable.Where вместо Queryable.Where.

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
    };

Первое «где» разрешается до Queryable.Where, а второе - «1011 *». Это не проблема или неэффективность, поскольку все выражение является частью подзапроса, поэтому оно будет все же отправлено поставщику запросов и (например,) переведено в SQL.

0 голосов
/ 18 февраля 2012

Хорошо, думаю, я понял:

Во-первых, получите метод через рефлексию, как сказал Игорь:

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

Затем я использовал другую версию Expression.Call для преодоления статического / экземпляра несовпадения:

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

и, наконец, приведите его к типизированному AsQueryable:

Expression final = Expression.Convert(buff, typeof(IQueryable<RelatedEntity>));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...