Доступ к результату из инструкции SELECT в хранимой процедуре с использованием EF Core и ExecuteSqlCommandAsync

Я пытаюсь использовать ExecuteSqlCommandAsync из EF Core, чтобы получить результат запроса select в несколько странной устаревшей хранимой процедуре с пустой RETURN заявление.

Хранимая процедура выглядит так:

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 заявление). Тем не менее, есть ли способ получить значение от NextValue используя EF Core? Похоже на FromSql может сработать, но я действительно заинтересован в получении единственного значения, а не сущности. Этот ответ, кажется, делает то, что я хочу, но я бы предпочел использовать EF Core, чем SqlCommand, если возможно.

3 ответа

Решение

Должно быть FromSql, Не возможно в EF Core 2.0. Возможно в EF Core 2.1, но немного сложнее, чем нужно, потому что запросы SQL, возвращающие примитивные типы, все еще не поддерживаются. Так что вам нужно использовать Query Type следующим образом:

Сначала вам нужно создать класс "типа запроса", который будет содержать результат:

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();

Вы могли бы сделать что-то вроде следующего. Я написал свой пример для запуска только на SQL, но концепция должна быть такой же для части C#. Я бы порекомендовал использовать SqlCommand, если вы когда-нибудь захотите, чтобы это было расширяемым. Вернуть также только все работы с целыми числами.

Поскольку вы можете вернуть любое целочисленное значение, вы можете просто сказать Return [integer] 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

Также вы должны изменить запрос на использование UPDATE … OUTPUT, чтобы два сеанса не получали одно и то же значение. НАПРИМЕР

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

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

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