Рейтинговые книги
Читем онлайн Язык программирования C#9 и платформа .NET5 - Троелсен Эндрю

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 422 423 424 425 426 427 428 429 430 ... 642

Тот же процесс применяется в случае использования метода Reference() для извлечения данных из навигационного свойства типа коллекции.

Выполнение низкоуровневых запросов SQL с помощью LINQ

Иногда получить корректный оператор LINQ для компилируемого запроса сложнее, чем просто написать код SQL напрямую. К счастью, инфраструктура EF Core располагает механизмом, позволяющим выполнять низкоуровневые операторы SQL в DbSet<T>. Методы FromSqlRaw() и FromSqlRawInterpolated() принимают строку, которая становится основой запроса LINQ. Такой запрос выполняется на стороне сервера.

Если низкоуровневый оператор SQL не является завершающим (скажем, хранимой процедурой, пользовательской функцией или оператором, который использует общее табличное выражение или заканчивается точкой с запятой), тогда в запрос можно добавить дополнительные операторы LINQ. Дополнительные операторы LINQ наподобие конструкций Include(), OrderBy() или Where() будут объединены с первоначальным низкоуровневым обращением SQL и любыми глобальными фильтрами запросов, после чего весь запрос выполнится на стороне сервера.

При использовании одного из вариантов FromSql*() запрос должен формироваться с использованием схемы базы данных и имени таблицы, а не имен сущностей. Метод FromSqlRaw() отправит строку в том виде, в каком она записана. Метод FromSqlInterpolated() применяет интерполяцию строк C# и каждая интерполированная строка транслируется в параметр SQL. В случае использования переменных вы должны использовать версию с интерполяцией для обеспечения дополнительной защиты, присущей параметризованным запросам.

Предположим, что для сущности Car установлен глобальный фильтр запросов. Тогда показанный ниже оператор LINQ получит первую запись Inventory со значением Id, равным 1, включит связанные данные Make и отфильтрует записи, касающиеся неуправляемых автомобилей:

var car = Context.Cars

  .FromSqlInterpolated($"Select * from dbo.Inventory where Id = {carId}")

  .Include(x => x.MakeNavigation)

  .First();

Механизм трансляции LINQ to SQL объединяет низкоуровневый оператор SQL с остальными операторами LINQ и выполняют следующий запрос:

SELECT TOP(1) [c].[Id], [c].[Color], [c].[IsDrivable], [c].[MakeId],

                           [c].[PetName], [c].[TimeStamp],

                           [m].[Id], [m].[Name], [m].[TimeStamp]

FROM (Select * from dbo.Inventory where Id = 1) AS [c]

INNER JOIN [dbo].[Makes] AS [m] ON [c].[MakeId] = [m].[Id]

WHERE [c].[IsDrivable] = CAST(1 AS bit)

Имейте в виду, что есть несколько правил, которые необходимо соблюдать в случае применения низкоуровневых запросов SQL с LINQ.

• Запрос SQL должен возвращать данные для всех свойств сущностного типа.

(window.adrunTag = window.adrunTag || []).push({v: 1, el: 'adrun-4-390', c: 4, b: 390})

• Имена столбцов должны совпадать с именами свойств, с которыми они сопоставляются (улучшение по сравнению с версией EF 6, где сопоставления игнорировались).

• Запрос SQL не может содержать связанные данные.

Пакетирование операторов

В EF Core значительно повышена производительность при сохранении изменений в базе данных за счет выполнения операторов в одном и более пакетов. В итоге объем взаимодействия между приложением и базой данных уменьшается, увеличивая производительность и потенциально сокращая затраты (скажем, для облачных баз данных, где за транзакции приходится платить).

Исполняющая среда EF Core пакетирует операторы создания, обновления и удаления с использованием табличных параметров. Количество операторов, которые пакетирует EF Core, зависит от поставщика баз данных. Например, в SQL Server пакетарование неэффективно для менее 4 и более 40 операторов. Независимо от количества пакетов все операторы по- прежнему выполняются в рамках транзакции. Размер пакета можно конфигурировать посредством DbContextOptions, но в большинстве ситуаций (если не во всех) рекомендуется позволять EF Core рассчитывать размер пакета самостоятельно.

Если бы вы вставляли четыре записи об автомобилях в одной транзакции, как показано ниже:

var cars = new List<Car>

{

  new Car { Color = "Yellow", MakeId = 1, PetName = "Herbie" },

  new Car { Color = "White", MakeId = 2, PetName = "Mach 5" },

  new Car { Color = "Pink", MakeId = 3, PetName = "Avon" },

  new Car { Color = "Blue", MakeId = 4, PetName = "Blueberry" },

};

Context.Cars.AddRange(cars);

Context.SaveChanges();

то исполняющая среда EF Core пакетировала бы операторы в одиночное обращение.

Вот как выглядел бы сгенерированный запрос:

exec sp_executesql N'SET NOCOUNT ON;

DECLARE @inserted0 TABLE ([Id] int, [_Position] [int]);

MERGE [Dbo].[Inventory] USING (

VALUES (@p0, @p1, @p2, 0),

(@p3, @p4, @p5, 1),

(@p6, @p7, @p8, 2),

(@p9, @p10, @p11, 3)) AS i ([Color], [MakeId], [PetName], _Position) ON 1=0

WHEN NOT MATCHED THEN

INSERT ([Color], [MakeId], [PetName])

VALUES (i.[Color], i.[MakeId], i.[PetName])

OUTPUT INSERTED.[Id], i._Position

INTO @inserted0;

SELECT [t].[Id], [t].[IsDrivable], [t].[TimeStamp] FROM [Dbo].[Inventory] t

INNER JOIN @inserted0 i ON ([t].[Id] = [i].[Id])

ORDER BY [i].[_Position];

',N'@p0 nvarchar(50),@p1 int,@p2 nvarchar(50),@p3 nvarchar(50),

@p4 int,@p5 nvarchar(50),@p6 nvarchar(50),@p7 int,@p8 nvarchar(50),

1 ... 422 423 424 425 426 427 428 429 430 ... 642
На этой странице вы можете бесплатно читать книгу Язык программирования C#9 и платформа .NET5 - Троелсен Эндрю бесплатно.
Похожие на Язык программирования C#9 и платформа .NET5 - Троелсен Эндрю книги

Оставить комментарий