2016-11-28 3 views
3

Я хотел бы сделать следующее вещь в Entity Framework кода первый:Внутренний метод Linq запроса, вернуть IQueryable

Data.GroupBy(x => x.Person.LastName) 
.Select(x => x.OtherGrouping(...)).Where(...).GroupBy(...)...etc 



public static class Extensions 
{ 

public static IQueryable<IGrouping<T, T>> OtherGrouping<T>(this IQueryable<T> collection /**** Here I want to pass consts and lambdas\expressions*****/) 
    { 
     // Grouping, filtration and other stuff here 
     return collection.GroupBy(x => x); 
    } 
} 

Проблема заключается в .net компилирует OtherGrouping метод, он не представляет, как выражение и не может быть преобразован в SQL.

Я нашел LinqKit библиотеку, которая предположительно поможет в таких случаях, но я не могу понять, как ее применять в моем конкретном случае. Он отлично работает для простых случаев, когда у меня есть выражение, например, x => x+2, но я получаю сообщение с возвратом IQueryable. Вероятно, можно полностью написать дерево выражений вручную, но я не хочу идти так глубоко.

Любые идеи, как это можно сделать или где я могу прочитать об этом?


Вот что я пытался сделать, основываясь на комментарий Роба

void Main() 
{ 
    AddressBases.GroupBy(x => x.ParentId).Select(x => new {x.Key, Items = x.AsQueryable().OtherGrouping(a => a.Country) }).Dump(); 

} 

public static class Extensions 
{ 
    public static IQueryable<IGrouping<TKey, TSource>> OtherGrouping<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector) 
    { 
     return source.Provider.CreateQuery<IGrouping<TKey, TSource>>(
      source.GroupBy(keySelector).Expression); 
    } 
} 

И я получил исключение:

NotSupportedException: LINQ to Entities does not recognize the method 'System.Linq.IQueryable`1[System.Linq.IGrouping`2[System.String,InsuranceData.EF.AddressBase]] OtherGrouping[AddressBase,String](System.Linq.IQueryable`1[InsuranceData.EF.AddressBase], System.Linq.Expressions.Expression`1[System.Func`2[InsuranceData.EF.AddressBase,System.String]])' method, and this method cannot be translated into a store expression. 

Не удалось выяснить еще, почему у меня есть OtherGrouping метод в дерево выражений? Метод OtherGrouping должен просто присоединить другую группу к выражению и передать ее поставщику, но не поместить себя в дерево.

+1

Вы должны были бы вызвать вызов поставщика запроса коллекции. Посмотрите здесь [https://github.com/Microsoft/referencesource/blob/master/System.Core/System/Linq/IQueryable.cs#L90) для примера. – Rob

+0

Спасибо @Rob, это хорошая идея взглянуть на исходный код LINQ. Причина моей глупости я пытался найти в источниках и ошибках EF. Я попробую сразу, может быть, вы можете опубликовать ответ вместо комментария, поэтому я могу его принять, и он станет более заметным для других людей. – Neir0

ответ

1

Ваш текущий код после правильного редактирования - то, где вы ошибетесь, является распространенной проблемой с синтаксическим сахаром, который мы даем C#.

Если вы пишете следующее:

void Main() 
{ 
    var res = Containers.OtherGrouping(c => c.ContainerID); 
    res.Expression.Dump(); 
    res.Dump(); 
} 

public static class Extensions 
{ 
    public static IQueryable<IGrouping<TKey, TSource>> OtherGrouping<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector) 
    { 
     return source.Provider.CreateQuery<IGrouping<TKey, TSource>>(
      source.GroupBy(keySelector).Expression 
     ); 
    } 
} 

Вы увидите, что мы получаем вывод:

Table(Container).GroupBy(c => c.ContainerID) 

И результат выполняется с вопросом, правильно группировать наш предикат. Однако вы вызываете OtherGrouping, а внутри выражение, когда вы передаете его Select. Давайте попробуем простой случай без OtherGrouping:

var res = Containers.OtherGrouping(c => c.ContainerID) 
    .Select(x => new 
    { 
     x.Key, 
     Items = x.GroupBy(c => c.ContainerID), 
    }); 
res.Expression.Dump(); 

Что вы предоставляете в Select это дерево выражения. То есть внутренний метод GroupBy никогда не вызывается. Если вы измените запрос на x.AsQueryable().OtherGrouping и положите точку останова в OtherGrouping, это произойдет только в первый раз.

В дополнение к этому, x является IEnumerable<T>, а не IQueryable<T>. Вызов AsQueryable() дает вам IQueryable, но он также дает вам новый поставщик запросов. Я не уверен на 100% относительно внутренней работы структуры сущности, но я бы сказал, что это неверно, поскольку у нас больше нет базы данных в качестве цели для провайдера. В самом деле, с linq2sql (используя LINQPad), мы получаем сообщение об ошибке .AsQueryable() не поддерживается.

Итак, что вы можете сделать? К сожалению, нет чистого способа сделать это. Так как дерево выражений уже построено до OtherGrouping имеет шанс, не имеет значения, что такое тело OtherGrouping.Нам нужно будет изменить дерево выражений после его создания, но до его выполнения.

Для этого вам нужно написать выражение посетитель, который будет искать .OtherGrouping выражения вызовов, и заменить его Queryable.GroupBy

+0

Возможно, мы можем сделать следующее: OtherGrouping должно возвращать выражение, и нам нужно построить выражение для выбора с вызовом метода OtherGrouping с помощью Invoke. Затем просто передайте его, чтобы выбрать и использовать LinqKit Expand magic change Вызовите вызов для фактического выражения. – Neir0

Смежные вопросы