Придерживаясь в книге темы автомобилей, предположим, что все записи Car, которые не являются управляемыми, должны отфильтровываться из нормальных запросов. Вот как можно добавить глобальный фильтр запросов с использованием Fluent API:
modelBuilder.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
При выполнении стандартного запроса 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;
К настоящему моменту не должен вызывать удивление тот факт, что сгенерированный оператор SQL включает фильтр для неуправляемых автомобилей: