TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.75K subscribers
Post #1882 1.8K
День 1539. #ЗаметкиНаПолях
Чистые SQL-запросы в EF Core
В EF7 появилась поддержка возврата скалярных типов с помощью SQL-запросов. В EF8 появилась поддержка запросов несопоставленных (unmapped) типов с помощью чистого SQL. Это то, что Dapper предлагает по умолчанию, и приятно видеть, что EF Core догоняет его.

EF Core и SQL
В EF8 чистые SQL-запросы могут возвращать любой тип без необходимости включать его в модель EF. Вы можете запрашивать несопоставленные типы с помощью методов SqlQuery и SqlQueryRaw. Метод SqlQuery использует интерполяцию строк для параметризации запроса, защищая от атак SQL-инъекцией.

Вот пример запроса, возвращающего список OrderSummary:
var start = new DateOnly(2023, 1, 1);
var orders = await dbContext
.Database
.SqlQuery<Order>(
@$"SELECT * FROM Orders
AS o WHERE o.Created >= {start}")
.ToListAsync();

В БД будет отправлен следующий запрос:
SELECT * FROM Orders AS o WHERE o.Created >= @p0

Тип, используемый для результата запроса, может иметь параметризованный конструктор. Имена свойств не обязательно должны совпадать с именами столбцов в базе данных, но они должны совпадать с именами полей в результирующем наборе. Вы также можете выполнять чистые SQL-запросы к представлениям, функциям и хранимым процедурам.

SQL + LINQ
Интересной особенностью SqlQuery является то, что он возвращает IQueryable, который можно дополнить с помощью LINQ. Можно добавить оператор Where после вызова SqlQuery:
var orders = await dbContext
.Database
.SqlQuery<Order>(
"SELECT * FROM Orders AS o")
.Where(o => o.Created >= start)
.ToListAsync();
Однако, в результате получится неоптимальный запрос:
SELECT s.Id, s.CustomerId, s.Price, s.Created
FROM (
SELECT * FROM Orders AS o
) AS s
WHERE s.Created >= @p0

Также возможно добавить OrderBy, Skip и Take:
var orders = await dbContext
.Database
.SqlQuery<Order>(
@$"SELECT * FROM Orders AS o
WHERE o.Created >= {start}")
.OrderBy(o => o.Id)
.Skip(10)
.Take(5)
.ToListAsync();

Получим следующий запрос:
SELECT s.Id, s.CustomerId, s.Price, s.Created
FROM (
SELECT * FROM Orders AS o WHERE o.Created >= @p0
) AS s
ORDER BY s.Id
OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY

Производительность аналогична LINQ-запросам с проекцией с помощью Select.

SQL-запросы для модификации данных
Если вы хотите изменить данные в базе данных с помощью SQL, вы обычно пишете запрос, который не возвращает результата. Запрос может быть UPDATE, DELETE или вызовом хранимой процедуры. Для этого можно использовать метод ExecuteSql:
dbContext.Database.ExecuteSql(
@$"UPDATE Orders SET Status = 5
WHERE Created >= {start}");

ExecuteSql также защищает от SQL-инъекций путем параметризации аргументов, как и SqlQuery. В EF7 можно написать приведённый выше запрос с помощью LINQ и метода ExecuteUpdate. Также есть метод ExecuteDelete для удаления записей.

Источник: https://www.milanjovanovic.tech/blog/ef-core-raw-sql-queries
  • 👍 15
More from @netdeveloperdiary
  1. Oct 7, 2026День 2807. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Окончание Нач…
  2. Oct 6, 2026🦈 Открытое собеседование на Middle C# | 6 октября, 19:00 МСК Приглашаем на открытое собес…
  3. Oct 6, 2026День 2806. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Начало Больши…
  4. Oct 5, 2026День 2805. #ЧтоНовенького #NET11 Аргументы в Выражениях Коллекций в C#15 В C#15 реализован…
  5. Oct 4, 2026День 2804. #ВопросыНаСобеседовании Марк Прайс предложил свой набор из 60 вопросов (как тех…
  6. Oct 3, 2026Post #3360
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →