Ответ 1
Я думаю, что очень сложно, если возможно, издеваться над поведением Entity Framework. Прежде всего, потому что это потребует глубокого знания всех особенностей и крайних случаев, когда linq-to-entites отличается от linq-to-objects. Как вы говорите: реальная задача - найти их. Позвольте мне указать на три основные области, не претендуя на то, чтобы быть даже почти исчерпывающим:
Случаи, когда Linq-to-Objects преуспевают, а Linq-to-Entities терпят неудачу:
-
.Выберите (x = > x.Property1.ToString()
. LINQ to Entities не распознает метод "System.String ToString()"... Это относится ко почти всем методам в собственных классах .Net и, конечно же, к собственным методам. Только несколько методов .Net будут переведены в SQL. См. Метод CLR для канонического отображения функций. С точки зрения EF 6.1 поддерживаетсяToString
. Но только безпараметрическая перегрузка. -
Пропустить()
без предшествующегоOrderBy
. -
За исключением
иIntersect
: может создавать чудовищные запросы, которые бросают Некоторая часть вашего оператора SQL слишком глубоко вложена. Перепишите запрос или разделите его на более мелкие запросы. -
Выберите (x = > x.Date1 - x.Date2)
: Аргументы DbArithmeticExpression должны иметь числовой общий тип. - (ваш случай)
.Where(p = > p.Category == category)
: В этом контексте поддерживаются только примитивные типы или типы перечислений. -
Nodes.Where(n = > n.ParentNodes.First(). Id == 1)
: Метод "Первый" может использоваться только как конечная операция запроса. -
context.Nodes.Last()
: LINQ to Entities не распознает метод "... Last..." . Это относится ко многим другим методам расширенияIQueryable
. См. Поддерживаемые и неподдерживаемые методы LINQ. - (см. комментарий Slauma ниже):
. Выбрать (x = > new A {Property1 = (x.BoolProperty? new B {BProp1 = x.Prop1, BProp2 = x.Prop2}: новый B {BProp1 = x.Prop1})})
: Тип "B" появляется в двух структурно несовместимых инициализациях внутри одного запроса LINQ to Entities... из here. -
context.Entities.Cast <IEntity>()
: Невозможно применить тип 'Entity' к типу 'IEntity'. LINQ to Entities поддерживает только листинг примитивных или перечисляемых типов EDM. -
.Выберите (p = > p.Category?.Name)
. Использование пустого распространения в выражении throws CS8072 Дерево выражений lambda может не содержать нулевой оператор распространения. Этот может быть исправлена в один прекрасный день. - Этот вопрос: Почему эта комбинация Select, Where и GroupBy вызывает исключение? заставило меня осознать факт что существуют даже целые построения запросов, которые не поддерживаются EF, в то время как L2O не будет иметь с ними никаких проблем.
Случаи, когда объекты Linq-to-Objects терпят неудачу, и Linq-to-Entities преуспевает:
-
.Выберите (p = > p.Category.Name)
: когдаp.Category
имеет значение null L2E возвращает значение null, но L2O выбрасывает Ссылка на объект не установленный в экземпляр объекта. Это невозможно устранить, используя нулевое распространение (см. выше). -
Nodes.Max(n = > n.ParentId.Value)
с некоторыми нулевыми значениями дляn.ParentId
. L2E возвращает максимальное значение, L2O throws Объект Nullable должен иметь значение. - Использование
EntityFunctions
(DbFunctions
с EF 6) илиSqlFunctions
.
Случаи, когда оба успеха/неудачи, но ведут себя по-другому:
-
Nodes.Include( "ParentNodes" )
: L2O не имеет реализации include. Он будет запускаться и возвращать узлы (еслиNodes
являетсяIQueryable
), но без родительских узлов. -
Nodes.Select(n = > n.ParentNodes.Max(p = > p.Id))
с некоторыми пустными коллекциямиParentNodes
: оба не работают, но с различные исключения. -
Nodes.Where(n = > n.Name.Contains( "par" ))
: L2O чувствителен к регистру, L2E зависит от сортировки базы данных (часто не чувствительной к регистру). -
node.ParentNode = parentNode
: с двунаправленным отношением, в L2E это также добавит node в коллекцию узлов родительского (связь fixup). Не в L2O. (См. Единичное тестирование двухсторонних отношений EF). - Работа для обхода нулевого распространения:
.Select(p = > p.Category == null? string.Empty: p.Category.Name)
: результат будет таким же, но сгенерированный SQL-запрос также содержит нулевую проверку и может быть сложнее оптимизировать. -
Nodes.AsNoTracking(). Выберите (n = > n.ParentNode
). Этот очень сложный!. СAsNoTracking
EF создает новый объектParentNode
для каждогоNode
, поэтому могут быть дубликаты. БезAsNoTracking
EF повторно использует существующиеParentNodes
, потому что теперь задействованы диспетчер состояний объекта и сущности.AsNoTracking()
можно вызвать в L2O, но он ничего не делает, поэтому никогда не будет никакой разницы с ним или без него.
А как насчет насмешливой ленивой/нетерпеливой загрузки и влияния жизненного цикла контекста на ленивые ошибки загрузки? Или эффект некоторых конструкций запроса на производительность (например, конструкции, которые запускают N + 1 SQL-запросы). Или исключения из-за дублирования или отсутствия ключей сущностей? Или исправление отношений?
Мое мнение: никто не собирается подделывать это. Наиболее тревожная область - это то, где L2O преуспевает, а L2E терпит неудачу. Теперь, какова ценность тестов зеленого блока? Ранее было сказано, что EF можно надежно тестировать только в тестах интеграции (например, здесь), и я склонен согласиться.
Однако это не означает, что мы должны забыть об модульных тестах в проектах с EF в качестве уровня данных. Существуют способы сделать это, но, Я думаю, не без интеграционных тестов.