Entity framework linq поиск

Обновлено: 06.07.2024

Для взаимодействия с источником данных Entity Framework Core использует технологию LINQ to Entities . В основе данной технологии лежит язык интегрированных запросов LINQ (Language Integrated Query). LINQ предлагает простой и интуитивно понятный подход для получения данных с помощью выражений, которые по форме близки выражениям языка SQL.

Хотя при работе с базой данных мы оперируем запросами LINQ, но, к примеру, база данных MS SQL Server понимает только запросы на языке SQL. Поэтому Entity Framework Core, используя выражения LINQ to Entities, транслирует их в определенные запросы, понятные для используемого источника данных.

Создание запросов

Создавать запросы мы можем двумя способами: через операторы LINQ и через методы расширения. Пусть для рассмотрения основных запросов в LINQ to Entities у нас будут следующие модели со связью один-ко-многим:

И пусть будет следующий контекст данных:

Вначале используем некоторые операторы LINQ:

Вначале здесь происходит добавление данных, если они отсутствуют. Далее примяются Linq-операторы:

В итоге мы получим следующий консольный вывод:

Теперь для получения данных используем методы расширения LINQ:

Оба запроса в итоге транслируются в одно и то же выражение sql:

All / AllAsync : возвращает true, если все элементы набора удовлятворяют определенному условию, иначе возвращает false

Any / AnyAsync : возвращает true, если хотя бы один элемент набора определенному условию

Average / AverageAsync : подсчитывает cреднее значение числовых значений в наборе

Contains / ContainsAsync : определяет, содержит ли набор определенный элемент

Count / CountAsync : подсчитывает количество элементов в наборе

First / FirstAsync : выбирает первый элемент коллекции

FirstOrDefault / FirstOrDefaultAsync : выбирает первый элемент коллекции или возвращает значение по умолчанию

Single / SingleAsync : выбирает единственный элемент коллекции, если коллекция содердит больше или меньше одного элемента, то генерируется исключение

SingleOrDefault / SingleOrDefaultAsync : выбирает первый элемент коллекции или возвращает значение по умолчанию

Select : определяет проекцию выбранных значений

Where : определяет фильтр выборки

OrderBy : упорядочивает элементы по возрастанию

OrderByDescending : упорядочивает элементы по убыванию

ThenBy : задает дополнительные критерии для упорядочивания элементов возрастанию

ThenByDescending : задает дополнительные критерии для упорядочивания элементов по убыванию

Join : соединяет два набора по определенному признаку

GroupBy : группирует элементы по ключу

Except : возвращает разность двух наборов, то есть те элементы, которые содератся только в одном наборе

Union : объединяет два однородных набора

Intersect : возвращает пересечение двух наборов, то есть те элементы, которые встречаются в обоих наборах элементов

Sum / SumAsync : подсчитывает сумму числовых значений в коллекции

Min / MinAsync : находит минимальное значение

Max / MaxAsync : находит максимальное значение

Take : выбирает определенное количество элементов с начала последовательности

TakeLast : выбирает определенное количество элементов с конца последовательности

Skip : пропускает определенное количество элементов с начала последовательности

SkipLast : пропускает определенное количество элементов с конца последовательности

TakeWhile : возвращает цепочку элементов последовательности, до тех пор, пока условие истинно

SkipWhile : пропускает элементы в последовательности, пока они удовлетворяют заданному условию, и затем возвращает оставшиеся элементы

ToList / ToListAsync : получения списка объектов

Для большинства методов определены асинхронные версии, при необходимости получать данные в асинхронном режиме, мы можем их задействовать:

Создайте экземпляр запроса ObjectQuery<T> на основе объектного контекста ObjectContext.

Преобразуйте стандартные операторы и выражения запросов LINQ в деревья команд.

Выполните запрос в виде дерева команд применительно к источнику данных. Все исключения, возникшие в источнике данных во время выполнения, передаются непосредственно клиенту.

Верните результаты запроса обратно клиенту.

Создание экземпляра ObjectQuery

Составление запросов

Экземпляры универсального класса ObjectQuery<T>, реализующего общий интерфейс IQueryable<T>, служат источниками данных для запросов по технологии LINQ to Entities. В запросе точно указывается, какие данные надо получить из источника. В запросе можно также указать, как следует сортировать, группировать и формировать возвращаемую информацию. В LINQ запрос хранится в переменной. Эта переменная запроса не выполняет никаких действий и не возвращает данные. Она только хранит информацию о запросе. После создания запроса его необходимо выполнить, чтобы получить данные.

Преобразование запросов

Для выполнения запроса в технологии LINQ to Entities на платформе Entity Framework запрос LINQ нужно преобразовать в дерево команд, которое можно выполнить на платформе Entity Framework.

запросы LINQ to Entities состоят из стандартных операторов запросов LINQ (таких как Select , Where и GroupBy ) и выражений (x > 10, Contact. LastName и т. д.). Операторы LINQ не определяются классом, а являются методами класса. В LINQ выражения могут содержать любые конструкции, допустимые для типов в пределах пространства имен System.Linq.Expressions, а по расширению - любые конструкции, которые могут быть представлены лямбда-функциями. Это надмножество выражений, допустимых для платформы Entity Framework, множество которых по определению ограничено допустимыми операциями над базой данных и поддерживается посредством ObjectQuery<T>.

На платформе Entity Framework и операторы, и выражения представляются одной иерархией типов, которая затем помещается в дерево команд. Дерево команд используется платформой Entity Framework для выполнения запроса. Если запрос LINQ не удается выразить в виде дерева команд, при преобразовании запроса будет создано исключение. Преобразование запросов LINQ to Entities включает два промежуточных преобразования - стандартных операторов запросов и выражений.

Для ряда стандартных операторов запроса LINQ не существует допустимых эквивалентов в технологии LINQ to Entities. Попытки использования этих операторов приведут к возникновению исключения в процессе перевода запроса. список поддерживаемых LINQ to Entities операторов см. в разделе поддерживаемые и неподдерживаемые методы LINQ (LINQ to Entities).

дополнительные сведения об использовании стандартных операторов запросов в LINQ to Entities см. в разделе стандартные операторы запросов в запросах LINQ to Entities.

Как правило, выражения технологии LINQ to Entities вычисляются на сервере, поэтому нельзя ожидать, что поведение выражений будет соответствовать семантике среды CLR. дополнительные сведения см. в разделе выражения в запросах LINQ to Entities.

Сведения о том, как вызовы методов среды CLR сопоставляются с каноническими функциями в источнике данных, см. в разделе метод CLR для сопоставления канонических функций.

сведения о вызове канонических, баз данных и пользовательских функций из запросов LINQ to Entities см. в разделе вызов функций в запросах LINQ to Entities.

Выполнение запроса

После создания пользователем запроса LINQ он преобразуется в представление, совместимое с Entity Framework (в виде дерева команд), которое затем выполняется для источника данных. Во время выполнения запроса все выражения (или компоненты) этого запроса вычисляются на клиенте либо на сервере. Это относится и к выражениям, используемым для материализации результатов и для проекции сущностей. Дополнительные сведения см. в разделе выполнение запроса. Сведения о том, как повысить производительность путем компиляции запроса один раз и последующего его выполнения несколько раз с различными параметрами, см. в разделе скомпилированные запросы (LINQ to Entities).

Материализация

Материализацией называется процесс возвращения результатов запроса клиенту в виде типов CLR. В LINQ to Entities результаты запроса никогда не возвращаются в виде записей данных. Для этого всегда используется базовый тип CLR, который был определен пользователем или платформой Entity Framework или создан компилятором (анонимный тип). Вся материализация объектов проводится платформой Entity Framework. Все ошибки, вызванные невозможностью сопоставления между платформой Entity Framework и средой CLR, вызовут создание исключений во время материализации объекта.

Результаты запроса обычно возвращаются в одной из следующих форм.

Коллекция, содержащая ноль или более типизированных объектов сущностей, либо проекция сложных типов, определенных в концептуальной модели.

В этом разделе рассматриваются различные способы запроса данных с помощью Entity Framework, включая LINQ и метод Find. Методы, представленные в этом разделе, также применимы к моделям, созданным с помощью Code First и конструктора EF.

Поиск сущностей с помощью запроса

DbSet и IDbSet реализуют IQueryable, поэтому их можно использовать как начальную точку для написания запроса LINQ к базе данных. Здесь мы не будем подробно рассматривать LINQ, но приведем несколько простых примеров:

Обратите внимание, что DbSet и IDbSet всегда создают запросы к базе данных и всегда используют круговой путь к базе данных, даже если возвращаемые сущности уже присутствуют в контексте. Запрос выполняется в базе данных, если:

Когда из базы данных возвращаются результаты, объекты, отсутствующие в контексте, присоединяются к контексту. Если объект уже есть в контексте, возвращается существующий объект (текущие и исходные значения свойств объекта в записи не переписываются значениями из базы данных).

Когда вы выполняете запрос, сущности, которые были добавлены в контекст, но еще не были сохранены в базе данных, не возвращаются в составе результирующего набора. Чтобы получить данные из контекста, см. раздел Локальные данные.

Если запрос не возвращает строки из базы данных, результатом будет пустая коллекция, а не NULL.

Поиск сущностей с помощью первичных ключей

Метод Find в классе DbSet использует значение первичного ключа, чтобы найти сущность, отслеживаемую контекстом. Если сущность не найдена в контексте, запрос отправляется в базу данных для поиска сущности там. Если сущность не найдена в контексте или в базе данных, возвращается значение NULL.

Метод Find имеет два важных отличия от запроса:

  • Круговой путь к базе данных будет использоваться только в том случае, если сущность с указанным ключом не найдена в контексте.
  • Метод Find возвращает сущности в состоянии "Добавлено". Это значит, что метод Find возвращает сущности, которые были добавлены в контекст, но еще не были сохранены в базе данных.

Поиск сущности по первичному ключу

В коде ниже приведено несколько примеров использования метода Find.

Поиск сущности по составному первичному ключу

Платформа Entity Framework позволяет сущностям иметь составные ключи, то есть ключи, состоящие из нескольких свойств. Например, вы можете создать сущность BlogSettings, которая представляет собой параметры пользователей для конкретного блога. Так как пользователю необходима только одна сущность BlogSettings для каждого блога, первичный ключ для BlogSettings может состоять из комбинации идентификатора блога и имени пользователя. Следующий код пытается найти BlogSettings по идентификатору = 3 и имени пользователя = johndoe1987:

Если у вас есть составные ключи, вам нужно использовать ColumnAttribute или текучий API, чтобы указать порядок свойств составного ключа. В вызове метода Find эти значения ключа должны указываться в том же порядке.

Рассмотрим, как в EF Core выполнять фильтрацию. Для этого используем модели из прошлой темы:

Where

Если необходимо отфильтровать получаемые данные, то для этого можно использовать метод Where . Например, выберем из бд всех пользователей, которые работают в компании "Google":

Аналогичный запос с помощью операторов LINQ:

EF.Functions.Like

Начиная с версии 2.0 в Entity Framework Core можно использовать метод EF.Functions.Like() . Он позволяет транслировать условие в выражение с оператором LIKE на стороне MS SQL Server. Метод принимает два параметра - оцениваемое выражение и шаблон, с которым сравнивается его значение. Например, найдем всех пользователей, в имени которых присутствует подстрока "Tom" (это могут быть "Tom", "Tomas", "Tomek", "Smith Tom"):

На стороне БД этот запрос будет транслироваться в следующую SQL-команду:

Для определения шаблона могут применяться ряд специальных символов подстановки:

% : соответствует любой подстроке, которая может иметь любое количество символов, при этом подстрока может и не содержать ни одного символа

_ : соответствует любому одиночному символу

[ ] : соответствует одному символу, который указан в квадратных скобках

[ - ] : соответствует одному символу из определенного диапазона

[ ^ ] : соответствует одному символу, который не указан после символа ^

Стоит отметить, что в качестве первого параметра метод принимает оцениваемое выражение в виде строки. В случае со свойством Name все просто, так как оно представляет тип string. Но если нам необходимо использовать в качестве оцеениваемого выражения другие свойства, то их следует привести к строке. Например, найдем всех пользователей у которых возраст (свойство Age) в диапазоне от 22 до 29:

Например, следующее выражение:

Подобным образом метод EF.Functions.Like() можно использовать с операторами LINQ:

Для выборки одного объекта мы можем использовать метод Find() . Данный метод не является методом Linq, он определен у класса DbSet:

При выполнении запроса он будет трансформироваться в следующее выражение SQL:

First/FirstOrDefault

Но в качестве альтернативы мы можем использовать методы Linq First()/FirstOrDefault() . Они получают первый элемент выборки, который соответствует определенному условию или набору условий. Использование метода FirstOrDefault() является более гибким, так как если выборка пуста, то он вернет значение null. А метод First() в той же ситуации выбросит ошибку.

По тому же принципу работают пары методов Single/SingleOrDefault и Last/LastOrDefault , которые извлекают соответственно любой единственный элемент и последний элемент последовательности.

Читайте также: