使用 EF Core 和 ExecuteSqlCommandAsync 从存储过程中的 SELECT 语句访问结果

Access result from SELECT statement in stored procedure using EF Core and ExecuteSqlCommandAsync

我正在尝试使用 EF Core 中的 ExecuteSqlCommandAsync 在具有空 RETURN 语句的有点奇怪的遗留存储过程中获取 select 查询的结果。

存储过程如下所示:

CREATE PROCEDURE [dbo].[SelectNextCounter]
(
@CounterName nvarchar(50)
)
AS
    UPDATE  counter
    SET NextValue = NextValue + 1
    WHERE   CounterName = @CounterName
    SELECT  NextValue
    FROM    counter
    WHERE   counterName = @CounterName
RETURN
GO

使用此代码,我能够访问存储过程的 RETURN 值(尽管我真正感兴趣的是 NextValue):

var counterName = new SqlParameter
{
    ParameterName = "@CounterName",
    Value = "CustomerNumber",
    SqlDbType = SqlDbType.NVarChar
};
var returnValue = new SqlParameter
{
    ParameterName = "@return_value",
    Direction = ParameterDirection.Output,
    SqlDbType = SqlDbType.Int
};

await _context
    .Database
    .ExecuteSqlCommandAsync(
        "EXEC @return_value = SelectNextCounter @CounterName", counterName, returnValue
    );

然后我可以通过 returnValue.Value 获取值(它始终为 0,因为 RETURN 语句为空)。但是,有什么方法可以使用 EF Core 从 NextValue 获取值吗?似乎 FromSql 可能有效,但我真的只对取回单个值感兴趣,而不是实体。 This answer 似乎可以满足我的要求,但如果可能的话,我宁愿使用 EF Core 而不是 SqlCommand

您可以执行如下操作。我只在 SQL 中将我的示例写到 运行,但 c# 部分的概念应该相同。如果您打算将其扩展,我建议您使用 SqlCommand。 Return 也只有 every 适用于整数。

因为你可以 return 任何整数值,你可以只说 Return [整数] https://docs.microsoft.com/en-us/sql/t-sql/language-elements/return-transact-sql?view=sql-server-2017

CREATE PROCEDURE [dbo].TestProc
AS
DECLARE @returnVal INT = (SELECT 2)
RETURN @returnVal
GO

DECLARE @newReturnVal INT
EXEC @newReturnVal = [dbo].TestProc
SELECT @newReturnVal

必须是FromSql。在 EF Core 2.0 中不可能。在 EF Core 2.1 中可能,但比需要的要复杂一些,因为 SQL 返回原始类型的查询仍然不受支持。所以你需要像这样使用 Query Type

首先您需要创建一个 "query type" class 来保存结果:

public class SelectNextCounterResult
{
    public int NextValue { get; set; }
}

其次,您需要在 OnModelCreating 覆盖中注册它:

modelBuilder.Query<SelectNextCounterResult>();

然后你就可以用它来调用你的SP了

var counterName = "CustomerNumber";
var result = await context.Query<SelectNextCounterResult>()
    .FromSql($"SelectNextCounter {counterName}")
    .FirstAsync();

您还应该将查询更改为使用 UPDATE … OUTPUT,这样两个会话就不会获得相同的值。 EG

CREATE PROCEDURE [dbo].[SelectNextCounter]
(
@CounterName nvarchar(50)
)
AS

    UPDATE  counter
    SET NextValue = NextValue + 1
    OUTPUT INSERTED.NextValue
    WHERE   CounterName = @CounterName

GO