Использование встроенных табличных функций с 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)
...

См. Ответ на следующий пост по ссылке для правильной реализации для связанных сценариев запросов.

Другие вопросы по тегам