[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 для находящейся в памяти записи Make. Ниже показан сгенерированный код SQL:
DECLARE @__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
(window.adrunTag = window.adrunTag || []).push({v: 1, el: 'adrun-4-390', c: 4, b: 390})
Если оператор LINQ для отдельного запроса слишком сложен или тестирование показывает, что производительность оказалась ниже, чем желаемая, тогда данные можно извлекать с использованием низкоуровневого оператора SQL через метод FromSqlRaw() или FromSqlInterpolated() класса DbSet<T>. Оператором SQL может быть встроенный оператор SELECT языка Т-SQL, хранимая процедура или табличная функция. Если запрос является открытым (например, оператор Т-SQL без завершающей точки с запятой), тогда операторы LINQ можно добавлять к вызову метода FromSqlRaw()/FromSqlInterpolated() для дальнейшего определения генерируемого запроса. Полный запрос выполняется на серверной стороне с объединением оператора SQL и кода SQL, сгенерированного операторами LINQ.
Если оператор завершен или содержит код SQL, который не может быть достроен (скажем, задействует общие табличные выражения), то такой запрос все равно выполняется на серверной стороне, но любая дополнительная фильтрация и обработка должна делаться на клиентской стороне как LINQ to Objects. Метод FromSqlRaw() выполняет запрос в том виде, в котором он набран. Метод FromSqlInterpolated() применяет интерполяцию строк C# и помещает интерполированные значения в параметры. В следующих тестах (из 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;