Доступ к результату из инструкции 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