Entity framework выборка из нескольких таблиц
При выборке данных выбирать нужно ровно столько сколько нужно за один раз. Никогда не извлекайте все данные из таблицы!
Встроенная проверка данных может быть выполнена, когда запрос вернул какие-то записи.
Вау, вау, вау, разогнался.
Самое время немного освежить знания по методам LINQ.
Давайте рассмотрим отличия между ToList AsEnumerable AsQueryable
Итак, ToList
- Выполняет запрос немедленно.
- Используйте .ToList() для форсирования получения данных и выхода из режима поздней загрузки (lazy loading), так что этот метод полезен перед тем как вы пройдетесь по данным.
- Выполнение с задержкой (lazy loading)
- Принимает параметр: Func
- Загружает каждую запись в память приложения и управляет фильтрует его (в том числе Where/Take/Skip приведут к тому, что, например запрос select * from Table1,
- загрузит результирующий набор в память, затем выберет первые N элементов)
- В этом случает отрабатывает схема: Linq-to-SQL + Linq-to-Object.
- Используйте IEnumerable для получения списка из базы данных в режиме поздней загрузки (lazy loading).
- Выполнение с задержкой (lazy loading)
- Преобразует Expression в T-SQL (с учетом специфики провайдера), удаленное исполняет запрос и возвращает результат в память приложения.
- Вот почему DbSet (в Entity Framework) также наследуется от AsQueryable чтобы получать эффективные запросы.
- Не загружает каждую запись, например если Take(5) это сгенерирует запрос вида «select top 5 * SQL» в фоновом режиме. Это означает, что этот подход более дружественный для SQL базы данных, и дает более скоростной результат.Так что AsQueryable() обычно работает быстрее, чем AsEnumerable() так как сначала генерирует T-SQL включающий в себя все условия Linq определённые вами.
- Используйте AsQueryable если хотите запрос к базе данных который может быть улучшен перед запуском на стороне сервера.
Пример использования AsQueryable в простейшеем случае:
Волшебство простого чтения
Если вам не нужно менять данные, только отобразить используйте .AsNoTracking() метод.
Медленная выборка
Быстрая выборка (только на чтение)
Чувствую, вы немного уже размялись?
Типы загрузки связанных данных
Для тех, кто забыл, что такое lazy loading.
Ленивая загрузка (Lazy loading) означает, что связанные данные прозрачно загружаются из базы данных при обращении к свойству навигации. Подробнее читаем тут .
И заодно, напомню о других типах загрузки связанных данных.
Активная загрузка (Eager loading) означает, что связанные данные загружаются из базы данных как часть первоначального запроса.
Внимание! Начиная с версии EF Core 3.0.0, каждое Include будет вызывать добавление дополнительного JOIN к запросам SQL, создаваемым реляционными поставщиками, тогда как предыдущие версии генерировали дополнительные запросы SQL. Это может значительно изменить производительность ваших запросов, в лучшую или в худшую сторону. В частности, запросы LINQ с чрезвычайно большим числом операторов включения могут быть разбиты на несколько отдельных запросов LINQ.
Явная загрузка (Explicit loading) означает, что связанные данные явно загружаются из базы данных позднее.
Рывок и прорыв! Двигаемся дальше?
Готовы ускориться еще больше?
Чтобы резко ускориться при выборке сложно структурированных и даже ненормализованных данных из реляционной базы данных есть два способа сделать это: используйте индексированные представления (1) или что еще лучше – предварительно подготовленные(вычисленные) данные в простой плоской форме для отображения (2).
(1) Индексированное представление в контексте MS SQL Server
Индексированное представление имеет уникальный кластеризованный индекс. Уникальный кластерный индекс хранится в SQL Server и обновляется, как и любой другой кластерный индекс. Индексированное представление является более значительным по сравнению со стандартными представлениями, которые включают сложную обработку большого количества строк, например, агрегирование большого количества данных или объединение множества строк.
Если на такие представления часто ссылаются в запросах, мы можем повысить производительность, создав уникальный кластеризованный индекс для представления. Для стандартного представления набор результатов не сохраняется в базе данных, вместо этого набор результатов вычисляется для каждого запроса, но в случае кластеризованного индекса набор результатов сохраняется в базе данных точно так же, как таблица с кластеризованным индексом. Запросы, которые специально не используют индексированное представление, могут даже выиграть от существования кластеризованного индекса из представления.
Представление индекса имеет определенную стоимость в виде производительности. Если мы создаем индексированное представление, каждый раз, когда мы изменяем данные в базовых таблицах, SQL Server должен поддерживать не только записи индекса в этих таблицах, но также и записи индекса в представлении. В редакциях SQL Server для разработчиков и предприятий оптимизатор может использовать индексы представлений для оптимизации запросов, которые не указывают индексированное представление. Однако в других выпусках SQL Server запрос должен включать индексированное представление и указывать подсказку NOEXPAND, чтобы получить преимущество от индекса в представлении.
(2) Если нужно сделать запрос, требующий отображения более трех уровней связанных таблиц в количестве три и более c повышенной CRUD нагрузкой, лучшим способом будет задуматься о том, чтобы периодически вычислять результирующий набор, сохранять его в таблице и использовать для отображения. Результирующая таблица, в которой будут сохраняться данные должна иметь Primary Key и индексы по полям поиска в LINQ.
Что насчет асинхронности?
Да! Используем ее где только можно! Вот пример:
И да, ничего не забыли для повышения производительности? Бууум!
Внимание: метод Do() добавлен для демонстрационных целей только, с целью указать работоспособность метода GetFederalDistrictsAsync(). Как правильно заметили мои коллеги тутнужен другой пример чистой асинхронности.
Напомню, когда выполняются запросы в Entity Framework Core.
При вызове операторов LINQ вы просто создаете представление запроса в памяти. Запрос отправляется в базу данных только после обработки результатов.
Ниже приведены наиболее распространенные операции, которые приводят к отправке запроса в базу данных.
- Итерация результатов в цикле for.
- Использование оператора, например ToList, ToArray, Single, Count.
- Привязка данных результатов запроса к пользовательскому интерфейсу.
Как же организовать код EF Core с точки зрения архитектуры приложения?
(1) C точки зрения архитектуры приложения, нужно обеспечить чтобы код доступа к вашей базе данных был изолирован / отделен в четко определенном месте (в изоляции). Это позволяет найти код базы данных, который влияет на производительность.
(2) Не смешивать код доступа к вашей базе данных с другими частями приложения, такими как пользовательский интерфейс или API. Таким образом, код доступа к базе данных можно изменить, не беспокоясь о других проблемах, не связанных с базой данных.
Как правильно и быстро сохранять данные с помощью SaveChanges?
Если вставляемые записи одинаковые имеет смысл использовать одну операцию сохранения на все записи.
Неправильно
Всегда есть исключения из правила. Если контекст транзакции сложный, то есть состоит из нескольких независимых операций, то можно выполнять сохранение после выполнения каждой операции. А еще правильней использовать асинхронное сохранение в транзакции.
Триггеры, вычисляемые поля, пользовательские функции и EF Core
Для снижения нагрузки на приложения содержащим EF Core имеет смысл применять простые вычисляемые поля и триггеры баз данных, но лучше этим не увлекаться, так как приложение может оказаться очень запутанным. А вот пользовательские функции могут быть очень полезны особенно при операциях выборки!
Параллелизм в EF Core
Если ты хочешь все запараллелить чтобы ускориться, то обломись: EF Core не поддерживает выполнение нескольких параллельных операций в одном экземпляре контекста. Следует подождать завершения одной операции, прежде чем запускать следующую. Для этого обычно нужно указать ключевое слово await в каждой асинхронной операции.
EF Core использует асинхронные запросы, которые позволяют избежать блокирования потока при выполнении запроса в базе данных. Асинхронные запросы важны для обеспечения быстрого отклика пользовательского интерфейса в толстых клиентах. Они могут также увеличить пропускную способность в веб-приложении, где можно высвободить поток для обработки других запросов. Вот пример:
А что вы знаете про компилированные запросы LINQ?
Если у вас есть приложение, которое многократно выполняет структурно похожие запросы в Entity Framework, вы часто можете повысить производительность, компилируя запрос один раз и выполняя его несколько раз с различными параметрами. Например, приложению может потребоваться получить всех клиентов в определенном городе; город указывается во время выполнения пользователем в форме. LINQ to Entities поддерживает использование для этой цели скомпилированных запросов.
Много примеров можно посмотреть тут.
Не делайте больших контекстов DbContext!
В общем так, я знаю многие из вас, если не почти все — lazy f_u__c_k__e_r__s и всю базу данных вы размещаете в один контекст, особенно это свойственно для подхода Database-First. И зря вы это делаете! Ниже приведен пример как можно разделить контекст. Конечно, таблицы соединения между контекстами придется дублировать, это минус. Так или иначе если у вас в контексте более 50 таблиц лучше подумать о его разделении.
Использование группировки контекста (pooling DdContext)
Как избежать лишних ошибок при CRUD в EF Core?
Никогда не делайте вычисления в вставку в одном коде. Всегда разделяйте формирование/подготовку объекта и его вставку/обновление. Просто разнесите по функциям: проверку введенных данным пользователем, вычисления необходимые предварительных данных, картирование или создание объекта, и собственно CRUD операцию.
Что делать, когда совсем дела плохо с производительностью приложения?
Пиво тут точно не поможет. А вот что поможет, так это разделение чтение и записи в архитектуре приложения с последующего разнесением по сокетам этих операций. Задумайтесь об использовании Command and Query Responsibility Segregation (CQRS) pattern, а также попробуйте, разделить таблицы на вставку и чтение между двумя базами данных.
Рассмотрим, как в EF Core выполнять фильтрацию. Для этого используем модели из прошлой темы:
2 Answers 2
I think it will be easier using syntax-based query:
And you should probably add orderby clause, to make sure Top(10) returns correct top ten items.
Thank you very much for the method; works clear but I would like to see the answer as I asked, thanks a lot again.
I prefer the other syntax except when it comes to joins. the other syntax is so much more convoluted. I don't get it at all.
This is untested, but I believe the syntax should work for a lambda query. As you join more tables with this syntax you have to drill further down into the new objects to reach the values you want to manipulate.
@Dan Out of curiosity is it just the not thought out at all naming conventions with the c, cm, and ccm, or simply the syntax required to perform the joins using linq and lambda that is hideous? If the former, and you'd like to edit the post to have a better layout, by all means have at it. I'm still new to entity framework and am still soaking in best practices so if you have suggestions to make this answer more eloquent for future users I'd appreciate the assistance.
I hadn't given the exact reason much thought when I commented, but certainly the naming conventions hurt readability (obv. copied from from OP). Also, the commas as the beginning of the line hurt readability a lot (subjective, for me), and whitespace / indentation could be slightly improved. I've submitted an edit with all of these (IMHO) improvements since you requested it.
Code formatting is often biased, but there are general things which most people agree look better. As for naming conventions, I used to call things really short names, but I can type plenty fast enough now (not even considering things like Intellisense) that the few characters saved is not worth the detriment in readability versus naming things verbosely e.g. "EntryID" vs. "EID", "combinedEntry" vs. "cm", etc. Eventually, someone else is going to read my code, and I'd rather they not grow hatred for me as a linear function of the number of lines of my code they've had to read / maintain.
I just don't get the arguments against commas starting lines. I'm a firm believer, as it makes commenting out individual clauses/arguments really easy. And it looks prettier :-)
Readability and semantics aside, this is a better answer than the usual from x in context.table join . in my opinion. This way you can build your Joins and Where clauses dynamically and add paging and stuff later.
Find/FindAsync
Для выборки одного объекта мы можем использовать метод Find()/FindAsync() . Данный метод не является методом Linq, он определен у класса DbSet:
При выполнении запроса он будет трансформироваться в следующее выражение SQL:
First/FirstOrDefault/FirstAsync/FirstOrDefaultAsync
Но в качестве альтернативы мы можем использовать методы Linq First()/FirstOrDefault() и их асинхронные версии FirstAsync()/FirstOrDefaultAsync() . Они получают первый элемент выборки, который соответствует определенному условию или набору условий. Использование метода FirstOrDefault() является более гибким, так как если выборка пуста, то он вернет значение null. А метод First() в той же ситуации выбросит ошибку.
С помощью данных методов можно просто получить первый объект из выборки:
Но в качестве параметра также можно передать условие. Тогда в выборку попадают только те объекты, которые соответствуют условию:
По тому же принципу работают пары методов Single/SingleOrDefault и Last/LastOrDefault , которые извлекают соответственно любой единственный элемент и последний элемент последовательности.
Рассмотрим, как в EF Core выполнять фильтрацию. Для этого используем модели из прошлой темы:
Where
Если необходимо отфильтровать получаемые данные, то для этого можно использовать метод Where . Например, выберем из бд всех пользователей, которые работают в компании "Google":
Аналогичный запос с помощью операторов LINQ:
Where
Если необходимо отфильтровать получаемые данные, то для этого можно использовать метод Where . Например, выберем из бд всех пользователей, которые работают в компании "Google":
Аналогичный запос с помощью операторов LINQ:
Where
Если необходимо отфильтровать получаемые данные, то для этого можно использовать метод Where . Например, выберем из бд всех пользователей, которые работают в компании "Google":
Аналогичный запос с помощью операторов LINQ:
EF.Functions.Like
С помощью метода EF.Functions.Like() можно задать условие запроса, которое транслируется в Entity Framework Core в выражение с оператором LIKE. Метод принимает два параметра - оцениваемое выражение и шаблон, с которым сравнивается его значение. Например, найдем всех пользователей, в имени которых присутствует подстрока "Tom" (это могут быть "Tom", "Tomas", "Tomek", "Smith Tom"):
Выражение EF.Functions.Like(p.Name!, "%Tom%")) означает, что мы ищем строки, где в свойстве Name содержиться подстрока "Tom". Поскольку Name представляет nullable-тип, то после названия свойства указывается оператор !.
На стороне БД этот запрос будет транслироваться в следующую SQL-команду:
Для определения шаблона могут применяться ряд специальных символов подстановки:
% : соответствует любой подстроке, которая может иметь любое количество символов, при этом подстрока может и не содержать ни одного символа
_ : соответствует любому одиночному символу
[ ] : соответствует одному символу, который указан в квадратных скобках
[ - ] : соответствует одному символу из определенного диапазона
[ ^ ] : соответствует одному символу, который не указан после символа ^
Стоит отметить, что в качестве первого параметра метод принимает оцениваемое выражение в виде строки. В случае со свойством Name все просто, так как оно представляет тип string. Но если нам необходимо использовать в качестве оцеениваемого выражения другие свойства, то их следует привести к строке. Например, найдем всех пользователей у которых возраст (свойство Age) в диапазоне от 20 до 29:
Например, следующее выражение:
Подобным образом метод EF.Functions.Like() можно использовать с операторами LINQ:
First/FirstOrDefault
Но в качестве альтернативы мы можем использовать методы Linq First()/FirstOrDefault() . Они получают первый элемент выборки, который соответствует определенному условию или набору условий. Использование метода FirstOrDefault() является более гибким, так как если выборка пуста, то он вернет значение null. А метод First() в той же ситуации выбросит ошибку.
По тому же принципу работают пары методов Single/SingleOrDefault и Last/LastOrDefault , которые извлекают соответственно любой единственный элемент и последний элемент последовательности.
Всем доброго времени суток, просьба помочь в деле, сижу уже третий день и не могу понять по какой причине в SQL Management Studio данные запросом выбираются корректно а при использовании EF иначе.
Необходимо сделать отображение объявления как на авто или авито не важно, с использованием изображений.
В общем имеется куча связанных таблиц более чем две ,это пользователи, комментарии к объявлению, марка авто, модель, характеристики, и изображения. Чтобы не гонять туда суда огромный запрос, либо на Linq либо на прямом SQL через SQlQuery я создал представление в БД, с использованием всех этих таблиц:
Сам запрос к этому представлению с выборкой по Id-у объявления в SQLManagement:
Суть в том что возвращаются разные данные в столбце PathPhoto я это сделал для того чтобы потом уже при возврате данных непосредственно на сервере в коде выбрать из этого столбца нужные мне значения для отображения на странице.
Но вот парадокс или мой косяк, где, уже все перепробовал, от работы с обычными типами и Linq до прямых запросов, данные возвращаются одни и те же это заметно по возвращаемым значениям столбца или уже свойства PаthPhoto в перечислении:
Использовать представления решил по той причине, что размер запроса реально велик, там учитываются более 10 столбцов, и несколько таблиц, думаю разумнее сделать его на сервере, выбор пал на представления. хотя как читал можно и функцию использовать которая возвращает мне нужные данные, правда опыта нет с этим пока, а уже сроки сдачи курсовой жмут. Вот и решил попробовать так. Еще фишка с изошками, ранее я делал отдельный запрос к таблице Images где выбирал нужные мне изо по Id-у, но подумал, что как то не совсем хорош получается сразу к одной странице два запроса делать. А если в бой вступает EF как я уже сам убедился. запрос просто раздувается как незнамо кто и что, и что еще страшнее становится вложенным.. когда, допустим делаю так :
Просьба описать, что можно решить, я уже просто опустил руки с этой возней:) Хотя бы на словах, в какую рыть сторону. И правильно ли я поступил в данной ситуации?
Я думаю, что на этапе создания прототипа, не надо заморачиваться с представлениями, лучше делайте чтобы быстрее получить работающий вариант. На самом деле время на обмен sql запросом гораздо меньше других факторов влияющих на производительность при использовании EF (change tracking, оптимальность запроса и т.д.) Все равно, если вы захотите чтобы очень быстро все работало - EF может и не подойти (dapper в помощь). По поводу вашей проблемы, без разбора запросов неясно что там происходит.
Благодарен за ответ, разобрался, данные подгружать стал простым запросом на выборку значений из БД, ведь не двоичный формат таскаю, а всего лишь три строковых значения. Немного повозился с кэшированием, через [OutputCache]
I'm trying to join three tables but I can't understand the method.
I completed join 2 tables
I would like to include tbl_Title table with TID PK and get Title field.
Please show a picture with expanded navigation properties. Navigation properties are ready-made joins.
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:
Читайте также: