Используйте встроенные табличные функции с Linq и Entity Framework - PullRequest
0 голосов
/ 24 октября 2018

Я создал встроенные функции с табличными значениями (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 Ответ

0 голосов
/ 24 октября 2018

Да, это возможно при использовании введенного в 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 }

Метод "select" внутри FromSql делает его компонуется , поэтому весь запрос транслируется в SQL и выполняется на стороне сервера.

Обновление: На самом деле это не работает при использовании в качестве коррелированного подзапроса, как в примере выше (см. Ссылка на ITVF вызывает «вторую операцию, начатую в этом контексте до завершения предыдущей операции», исключение ).Его можно использовать, только если переданы постоянные / переменные параметры, такие как

from r in db.VehicleRepairStatus(123)
...

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

...