简体   繁体   English

在 Linq 和 Entity Framework Core 中使用内联表值函数

[英]Use a Inline Table-Valued Functions with Linq and Entity Framework Core

I created an Inline Table-Valued Functions (ITVF) in SQL Server that returns a table of values (query simplified for discussion purposes):我在 SQL Server 中创建了一个内联表值函数 (ITVF),它返回一个值表(为了讨论而简化了查询):

CREATE FUNCTION dbo.VehicleRepairStatus()
RETURNS TABLE
AS
   RETURN
       SELECT VehicleID, CurrentStatus 
       FROM VehicleRepairHistory
       ...

Which I can reference in a query:我可以在查询中引用:

SELECT   
    v.ID, v.Name,
    r.CurrentStatus
FROM  
    Vehicle v
LEFT OUTER JOIN 
    dbo.VehicleRepairStatus() r on v.ID = r.VehicleID

I'd like to be able to use it in Linq query:我希望能够在 Linq 查询中使用它:

var vehicles = await _databaseContext.Vehicles
    .Join() // join ITVF here?
    .Where(v => v.Type == 'Bus' )
    .OrderBy(v => v.Name)
    .ToAsyncList();

At some point, I may change the ITVF to include a parameter:在某些时候,我可能会更改 ITVF 以包含一个参数:

CREATE FUNCTION dbo.VehicleRepairStatus(@id AS INT)
RETURNS TABLE
AS
RETURN

  SELECT VehicleID, CurrentStatus 
  FROM   VehicleRepairHistory
  ...
  WHERE  VehicleID = @id

And call like a scalar:并像标量一样调用:

SELECT   v.ID, v.Name
        ,(SELECT val FROM dbo.VehicleRepairStatus(v.ID)) AS CurrentStatus
FROM  Vehicle v

Linq query:林克查询:

var vehicles = await _databaseContext.Vehicles
    .Select( )  // call ITVF here?
    .Where(v => v.Type == 'Bus' )
    .OrderBy(v => v.Name)
    .ToAsyncList();

Is either approach possible?这两种方法都可能吗?

Yes, it's possible by utilizing the EF Core 2.1 introduced query types (starting from EF Core 3.0, consolidated with entity types and now called keyless entity types ).是的,可以通过利用 EF Core 2.1 引入的 查询类型(从 EF Core 3.0 开始,与实体类型合并,现在称为无键实体类型)。 Following are the required steps:以下是必需的步骤:

First, create a class to hold the TVF record (update it with the correct data types):首先,创建一个类来保存 TVF 记录(用正确的数据类型更新它):

public class VehicleRepairStatus
{
    public int VehicleID { get; set; }
    public int CurrentStatus { get; set; }
}

Then register it in your OnModelCreating :然后在您的OnModelCreating注册它:

EF Core 2.x: EF 核心 2.x:

modelBuilder.Query<VehicleRepairStatus>();

EF Core 3.x: EF 核心 3.x:

modelBuilder.Entity<VehicleRepairStatus>().HasNoKey().ToView(null);

Then expose it from your db context using a combination of Query and FromSql methods (EF Core 2.x):然后使用QueryFromSql方法(EF Core 2.x)的组合从您的数据库上下文中公开它:

public IQueryable<VehicleRepairStatus> VehicleRepairStatus(int id) => 
    Query<VehicleRepairStatus>().FromSql($"select * from VehicleRepairStatus({id})");

or Set and FromSqlInterpolated (EF Core 3.x):SetFromSqlInterpolated (EF Core 3.x):

public IQueryable<VehicleRepairStatus> VehicleRepairStatus(int id) => 
    Set<VehicleRepairStatus>().FromSqlInterpolated($"select * from VehicleRepairStatus({id})");

And that's all.就这样。

Now you can use it inside your LINQ queries like any other IQueryable<T> returning method, for instance:现在,您可以像任何其他IQueryable<T>返回方法一样在 LINQ 查询中使用它,例如:

from v in db.Vehicles
from r in db.VehicleRepairStatus(v.ID)
select new { v.ID, v.Name, r.CurrentStatus }

The "select" inside FromSql method makes it composable , so the whole query is translated to SQL and executed server side. FromSql方法中的“选择”使其可组合,因此整个查询被转换为 SQL 并在服务器端执行。

Update: Actually this doesn't work when used as correlated subquery like the above example (see Reference to an ITVF raises a "second operation started on this context before a previous operation completed" exception ).更新:实际上,当用作上述示例的相关子查询时,这不起作用(请参阅对 ITVF 的引用引发“在上一个操作完成之前在此上下文上启动的第二个操作”异常)。 It could be used only if passing constant/variable parameters like只有在传递常量/变量参数时才能使用它,例如

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

See the answer to the follow up post from the link for correct implementation for correlated query scenarios.有关相关查询场景的正确实现,请参阅链接中后续帖子的答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM