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

ЖАНРЫ

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

Глобальные фильтры запросов позволяют добавлять конструкцию

where
во все запросы LINQ для определенной сущности. Например, распространенное проектное решение для баз данных предусматривает использование "мягкого" удаления вместо "жесткого". В таблицу добавляется поле, указывающее состояние удаления записи. Если запись "удалена", то значение поля устанавливается в
true
(или
1
), но запись из базы данных не убирается. Прием называется "мягким" удалением. Чтобы отфильтровать записи, повергнувшиеся "мягкому" удалению, от тех, которые обрабатывались
нормальными операциями, каждая конструкция
where
обязана проверять значение поля с состоянием удаления записи. Включение такого фильтра в каждый запрос может занять много времени, да и не забыть о нем довольно проблематично.

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

where
в каждый запрос.

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

Car
, которые не являются управляемыми, должны отфильтровываться из нормальных запросов. Вот как можно добавить глобальный фильтр запросов с использованием Fluent API:

modelBuilder.Entity<Car>(entity =>

{

entity.HasQueryFilter(c => c.IsDrivable == true);

entity.Property(p => p.IsDrivable).HasField("_isDrivable").

HasDefaultValue(true);

});

Благодаря такому глобальному фильтру запросов запросы, вовлекающие сущность

Car
, будут автоматически отфильтровывать неуправляемые автомобили. Скажем, запуск следующего запроса LINQ:

var cars = Context.Cars.ToList;

приводит к выполнению показанного ниже оператора SQL:

SELECT [i].[Id], [i].[Color], [i].[IsDrivable], [i].[MakeId],

[i].[PetName], [i].[TimeStamp]

FROM [Dbo].[Inventory] AS [i]

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

Если вам нужно просмотреть отфильтрованные записи, тогда добавьте в запрос LINQ вызов

IgnoreQueryFilters
, который отключает глобальные фильтры запросов для каждой сущности в запросе LINQ. Запуск представленного далее запроса LINQ:

var cars = Context.Cars.IgnoreQueryFilters.ToList;

инициирует выполнение следующего оператора SQL:

SELECT [i].[Id], [i].[Color], [i].[IsDrivable], [i].[MakeId],

[i].[PetName], [i].[TimeStamp]

FROM [Dbo].[Inventory] AS [i]

Важно

отметить, что вызов
IgnoreQueryFilters
удаляет фильтр запросов для всех сущностей в запросе LINQ, в том числе и тех, которые задействованы в вызовах
Include
или
Thenlnclude
.

Глобальные фильтры запросов на навигационных свойствах

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

Car
, представляющий неуправляемый автомобиль. Фильтр создается на навигационном свойстве
CarNavigation
сущности
Order
:

modelBuilder.Entity<Order>.HasQueryFilter(e => e.CarNavigation.IsDrivable);

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

// Код C#

var orders = Context.Orders.ToList;

/* Сгенерированный запрос SQL */

SELECT [o].[Id], [o].[CarId], [o].[CustomerId], [o].[TimeStamp]

FROM [Dbo].[Orders] AS [o]

INNER JOIN (SELECT [i].[Id], [i].[IsDrivable]

FROM [Dbo].[Inventory] AS [i]

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

ON [o].[CarId] = [t].[Id]

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

Для удаления фильтра запросов используйте вызов

IgnoreQueryFilters
. Вот как выглядит модифицированный оператор LINQ и сгенерированный запрос SQL:

// Код C#

var orders = Context.Orders.IgnoreQueryFilters.ToList;

/* Сгенерированный запрос SQL */

SELECT [o].[Id], [o].[CarId], [o].[CustomerId], [o].[TimeStamp]

FROM [Dbo].[Orders] AS [o]

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

Явная загрузка с глобальными фильтрами запросов

Глобальные фильтры запросов действуют и при явной загрузке связанных данных. Например, если вы хотите загрузить записи

Car
для
Make
, то фильтр
IsDrivable
предотвратит загрузку в память записей, представляющих неуправляемые автомобили. В качестве примера взгляните на следующий фрагмент кода:

var make = Context.Makes.First(x => x.Id == makeId);

Context.Entry(make).Collection(c=>c.Cars).Load;

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