Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

Сравнение отслеживаемых и неотслеживаемых запросов

При чтении информации из базы данных в экземпляр

DbSet<T>
сущности (по умолчанию) отслеживаются компонентом
ChangeTracker
, что обычно и требуется в приложении. Как только начинается отслеживание экземпляра компонентом
ChangeTracker
, любые последующие обращения к базе данных за тем же самым элементом (на основе первичного ключа) будут приводить к обновлению элемента, а не к его дублированию.

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

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

Чтобы загрузить экземпляр

DbSet<T>
, не помещая данные в
ChangeTracker
, добавьте к оператору LINQ вызов
AsNoTracking
, который указывает EF Core о необходимости извлечения данных без их помещения в
ChangeTracker
. Например, для загрузки записи
Car
без ее добавления в
ChangeTracker
введите следующий код:

public virtual Car? FindAsNoTracking(int id)

=> Table.AsNoTracking.FirstOrDefault(x => x.Id == id);

Преимущество показанного кода в том, что он не увеличивает нагрузку на память, но с ним связан и недостаток: дополнительные вызовы для извлечения той же самой записи

Car
создадут ее добавочные копии. За счет потребления большего объема памяти и чуть более длительного выполнения запрос можно модифицировать, чтобы гарантировать наличие только одного экземпляра несопоставленной сущности
Car
:

public virtual Car? FindAsNoTracking(int id)

=> Table.AsNoTrackingWithIdentityResolution.FirstOrDefault(x => x.Id == id);

Важные функциональные средства EF Core

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

На заметку! Фрагменты кода в текущем разделе взяты прямо из завершенной библиотеки доступа к данным

AutoLot
, которая будет построена в следующей главе.

Обработка значений, генерируемых базой данных

Помимо отслеживания изменений и генерации запросов SQL из LINQ существенным преимуществом использования EF Core по сравнению с низкоуровневой инфраструктурой ADO.NET является гладкая обработка значений, генерируемых базой данных. После добавления или обновления сущности исполняющая среда EF Core запрашивает любые данные, генерируемые базой, и автоматически обновляет сущность с применением корректных значений. При работе с низкоуровневой инфраструктурой ADO.NET это пришлось бы делать самостоятельно.

Например, таблица

Inventory
имеет целочисленный первичный ключ, который определяется в SQL Server как столбец
Identity
. Столбцы
Identity
заполняются СУБД SQL Server уникальными числами (из последовательности) при добавлении записи и не могут обновляться во время обычных
обновлений (исключая особый случай
IDENTITY_INSERT
). Кроме того, таблица
Inventory
содержит столбец
TimeStamp
для проверки параллелизма. Проверка параллелизма рассматривается далее, а пока достаточно знать, что столбец
TimeStamp
поддерживается SQL Server и обновляется при любом действии добавления или редактирования.

В качестве примера возьмем добавление новой записи

Car
в таблицу
Inventory
. В приведенном ниже коде создается новый экземпляр
Car
, который добавляется к экземпляру
DbSet<Car>
класса, производного от
DbContext
, и вызывается метод
SaveChanges
для сохранения данных:

var car = new Car

{

Color = "Yellow",

MakeId = 1,

PetName = "Herbie"

};

Context.Cars.Add(car);

Context.SaveChanges;

При выполнении метода

SaveChanges
в таблицу вставляется новая запись, после чего исполняющей среде EF Core возвращаются значения
Id
и
TimeStamp
из таблицы, причем свойства сущности обновляются надлежащим образом:

INSERT INTO [Dbo].[Inventory] ([Color], [MakeId], [PetName])

VALUES (N'Yellow', 1, N'Herbie');

SELECT [Id], [TimeStamp]

FROM [Dbo].[Inventory]

WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity;

На заметку! Фактически EF Core выполняет параметризованные запросы, но приводимые примеры упрощены ради читабельности.

Поступать так можно и при добавлении в базу данных множества элементов. Исполняющей среде EF Core известно, каким образом связывать значения с корректными сущностями. Когда записи обновляются, то значения первичных ключей уже известны, так что в нашем примере с

Car
запрашивается и возвращается только значение
TimeStamp
.

Проверка параллелизма

Проблемы с параллелизмом возникают, когда два отдельных процесса (пользователя или системы) пытаются почти одновременно обновить ту же самую запись. Скажем, пользователи User 1 и User 2 получают данные для Customer А. Пользователь User 1 обновляет адрес и сохраняет изменения. Пользователь User 2 обновляет кредитный риск и пытается сохранить ту же запись. Если сохранение для пользователя User 2 сработало, тогда изменения от пользователя User 1 будут отменены, т.к. после того, как пользователь User 2 извлек запись, адрес изменился. Другой вариант — отказ сохранения для пользователя User 2, когда изменения для User 1 записываются, но изменения для User 2 — нет.

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

Поделиться с друзьями: