Язык программирования C#9 и платформа .NET5
[InlineData(2,1)][InlineData(3,1)][InlineData(4,2)][InlineData(5,3)][InlineData(6,1)]public void ShouldGetAllCarsForAMakeExplicitlyWithQueryFilters(int makeId, int carCount){var make = Context.Makes.First(x => x.Id == makeId);IQueryable<Car> query = Context.Entry(make).Collection(c => c.Cars).Query();var qs = query.ToQueryString();query.Load();Assert.Equal(carCount,make.Cars.Count());}Этот тест похож на тест
из раздела "Фильтрация записей" ранее в главе. Однако вместо того, чтобы просто получить записиShouldGetTheCarsByMake(), которые имеют определенное значениеCar, текущий тест сначала получает записьMakeIdи затем явно загружает записиMakeдля находящейся в памяти записиCar. Ниже показан сгенерированный код SQL:MakeDECLARE @__p_0 int = 5;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)) AND ([i].[MakeId] = @__p_0)Обратите внимание на то, что фильтр запросов по-прежнему применяется, хотя главной сущностью в запросе является запись
. Для отключения фильтров запросов при явной загрузке записей вызовитеMakeв сочетании с методомIgnoreQueryFilters(). Вот тест, который отключает фильтры запросов (находится вQuery()):MakeTests.cs[Theory][InlineData(1, 2)][InlineData(2, 1)][InlineData(3, 1)][InlineData(4, 2)][InlineData(5, 3)][InlineData(6, 1)]public void ShouldGetAllCarsForAMakeExplicitly(int makeId, int carCount){var make = Context.Makes.First(x => x.Id == makeId);IQueryable<Car> query =Context.Entry(make).<b>Collection(c => c.Cars).Query().IgnoreQueryFilters()</b>;var qs = query.IgnoreQueryFilters().ToQueryString();query.Load();Assert.Equal(carCount, make.Cars.Count());}Выполнение запросов SQL с помощью LINQ
Если оператор LINQ для отдельного запроса слишком сложен или тестирование показывает, что производительность оказалась ниже, чем желаемая, тогда данные можно извлекать с использованием низкоуровневого оператора SQL через метод
илиFromSqlRaw()классаFromSqlInterpolated(). Оператором SQL может быть встроенный операторDbSet<T>языка Т-SQL, хранимая процедура или табличная функция. Если запрос является открытым (например, оператор Т-SQL без завершающей точки с запятой), тогда операторы LINQ можно добавлять к вызову методаSELECTдля дальнейшего определения генерируемого запроса. Полный запрос выполняется на серверной стороне с объединением оператора SQL и кода SQL, сгенерированного операторами LINQ.FromSqlRaw()/FromSqlInterpolated()Если оператор завершен или содержит код SQL, который не может быть достроен (скажем, задействует общие табличные выражения), то такой запрос все равно выполняется на серверной стороне, но любая дополнительная фильтрация и обработка должна делаться на клиентской стороне как LINQ to Objects. Метод
выполняет запрос в том виде, в котором он набран. МетодFromSqlRaw()применяет интерполяцию строк C# и помещает интерполированные значения в параметры. В следующих тестах (изFromSqlInterpolated()) демонстрируются примеры использования обоих методов с глобальными фильтрами запросов и без них:CarTests.cs[Fact]public void ShouldNotGetTheLemonsUsingFromSql(){var entity = Context.Model.FindEntityType($"{typeof(Car).FullName}");var tableName = entity.GetTableName();var schemaName = entity.GetSchema();<b> var cars = Context.Cars.FromSqlRaw($"Select * from {schemaName}.{tableName}")</b><b> .ToList();</b>Assert.Equal(9, cars.Count);}[Fact]public void ShouldGetTheCarsUsingFromSqlWithIgnoreQueryFilters(){var entity = Context.Model.FindEntityType($"{typeof(Car).FullName}");var tableName = entity.GetTableName();var schemaName = entity.GetSchema();<b> var cars = Context.Cars.FromSqlRaw($"Select * from {schemaName}.{tableName}")</b><b> .IgnoreQueryFilters().ToList();</b>Assert.Equal(10, cars.Count);}[Fact]public void ShouldGetOneCarUsingInterpolation(){var carId = 1;