Язык программирования C#9 и платформа .NET5
// Expression<Func<Customer>> - это лямбда-выражение.Expression<Func<Customer, bool>> expression = x => x.Id == 10;// Возвращается null, когда ничего не найдено.var customer = Context.Customers.SingleOrDefault(expression);Assert.Null(customer);}Предыдущий запрос LINQ транслируется в такой код SQL:
SELECT TOP(1) [c].[Id], [c].[TimeStamp], [c].[FirstName], [c].[FullName],[c].[LastName]FROM [Dbo].[Customers] AS [c]WHERE [c].[Id] = 10Глобальные фильтры запросов
Вспомните о наличии для сущности
глобального фильтра запросов, который отбрасывает данные об автомобилях со значением свойстваCar, равнымIsDrivable:falsemodelBuilder.Entity<Car>(entity =>{entity.HasQueryFilter(c => c.IsDrivable);...});Откройте файл класса
и добавьте показанный далее тест (все тесты в последующих разделах находятся вCarTests.cs, если не указано иначе):СаrTests.cs[Fact]public void ShouldReturnDrivableCarsWithQueryFilterSet(){IQueryable<Car> query = Context.Cars;var qs = query.ToQueryString();var cars = query.ToList();Assert.NotEmpty(cars);Assert.Equal(9, cars.Count);}Также вспомните, что в процессе инициализации данных были созданы 10 записей об автомобилях,из которых один установлен как неуправляемый. При запуске запроса применяется глобальный фильтр запросов и выполняется следующий код 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)На заметку! Как вскоре будет показано, глобальные фильтры запросов также применяются при загрузке связанных сущностей и при использовании методов
иFromSqlRaw().FromSqlInterpolated()Отключение глобальных фильтров запросов
Чтобы отключить глобальные фильтры запросов для сущностей в запросе, добавьте к запросу LINQ вызов метода
. Он заблокирует все фильтры для всех сущностей в запросе. Если есть несколько сущностей с глобальными фильтрами запросов и некоторые фильтры сущностей нужны, тогда потребуется поместить их в методыIgnoreQueryFilters()оператора LINQ. Добавьте в файл классаWhere()приведенный ниже тест, который отключает фильтр запросов и возвращает все записи:CarTests.cs[Fact]public void ShouldGetAllOfTheCars(){IQueryable<Car> query = Context.Cars.<b>IgnoreQueryFilters()</b>;var qs = query.ToQueryString();var cars = query.ToList();Assert.Equal(10, cars.Count);}Как и можно было ожидать, в сгенерированном коде SQL больше нет конструкции
, устраняющей записи для неуправляемых автомобилей:WHERESELECT [i].[Id], [i].[Color], [i].[IsDrivable], [i].[MakeId], [i].[PetName],[i].[TimeStamp]FROM [dbo].[Inventory] AS [i]Фильтры запросов для навигационных свойств
Помимо глобального фильтра запросов для сущности
был добавлен фильтр запросов к свойствуCarсущностиCarNavigation:OrdermodelBuilder.Entity<Order>().HasQueryFilter(e => e.CarNavigation!.IsDrivable);Чтобы увидеть его в действии, добавьте в файл класса
следующий тест:OrderTests.cs[Fact]public void ShouldGetAllOrdersExceptFiltered(){var query = Context.Orders.AsQueryable();var qs = query.ToQueryString();var orders = query.ToList();Assert.NotEmpty(orders);Assert.Equal(4,orders.Count);}Вот сгенерированный код 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)\r\n) AS [t]ON [o].[CarId] = [t].[Id]WHERE [t].[IsDrivable] = CAST(1 AS bit)Поскольку навигационное свойство
является обязательным, механизм трансляции запросов использует конструкциюCarNavigation, исключая записиINNER JOIN, гдеOrderсоответствует неуправляемому автомобилю. Для возвращения всех записей добавьте в запрос LINQ вызовCar.IgnoreQueryFilters()