Использование встроенных табличных функций с Linq и Entity Framework
Я создал встроенные функции с табличными значениями (ITVF) в SQL Server, которые возвращают таблицу значений (запрос упрощен для обсуждения):
CREATE FUNCTION dbo.VehicleRepairStatus()
RETURNS TABLE
AS
RETURN
SELECT VehicleID, CurrentStatus
FROM VehicleRepairHistory
...
На что я могу ссылаться в запросе:
SELECT
v.ID, v.Name,
r.CurrentStatus
FROM
Vehicle v
LEFT OUTER JOIN
dbo.VehicleRepairStatus() r on v.ID = r.VehicleID
Я хотел бы иметь возможность использовать его в запросе Linq:
var vehicles = await _databaseContext.Vehicles
.Join() // join ITVF here?
.Where(v => v.Type == 'Bus' )
.OrderBy(v => v.Name)
.ToAsyncList();
В какой-то момент я могу изменить ITVF для включения параметра:
CREATE FUNCTION dbo.VehicleRepairStatus(@id AS INT)
RETURNS TABLE
AS
RETURN
SELECT VehicleID, CurrentStatus
FROM VehicleRepairHistory
...
WHERE VehicleID = @id
И звони как скаляр
SELECT v.ID, v.Name
,(SELECT val FROM dbo.VehicleRepairStatus(v.ID)) AS CurrentStatus
FROM Vehicle v
Запрос Linq:
var vehicles = await _databaseContext.Vehicles
.Select( ) // call ITVF here?
.Where(v => v.Type == 'Bus' )
.OrderBy(v => v.Name)
.ToAsyncList();
Возможен ли какой-либо подход?
1 ответ
Да, это возможно благодаря использованию введенных в EF Core 2.1 типов запросов. Ниже приведены необходимые шаги:
Сначала создайте класс для хранения записи TVF (обновите ее, указав правильные типы данных):
public class VehicleRepairStatus
{
public int VehicleID { get; set; }
public int CurrentStatus { get; set; }
}
Затем зарегистрируйте его в своем OnModelCreating
:
modelBuilder.Query<VehicleRepairStatus>();
Затем выставьте его из контекста БД, используя комбинацию Query
а также FromSql
методы:
public IQueryable<VehicleRepairStatus> VehicleRepairStatus(int id) =>
Query<VehicleRepairStatus>().FromSql($"select * from VehicleRepairStatus({id})");
И это все.
Теперь вы можете использовать его внутри ваших запросов LINQ, как и любой другой IQueryable<T>
метод возврата, например:
from v in db.Vehicles
from r in db.VehicleRepairStatus(v.ID)
select new { v.ID, v.Name, r.CurrentStatus }
"Выбрать" внутри FromSql
Метод делает его компонуемым, поэтому весь запрос транслируется в SQL и выполняется на стороне сервера.
Обновление: на самом деле это не работает, когда используется как коррелированный подзапрос, как в примере выше (см. Ссылка на ITVF вызывает исключение "вторая операция, запущенная в этом контексте до завершения предыдущей операции"). Это может быть использовано только в том случае, если передаются постоянные / переменные параметры типа
from r in db.VehicleRepairStatus(123)
...
См. Ответ на следующий пост по ссылке для правильной реализации для связанных сценариев запросов.