我有以下代码:
object[] parameters =
{
new SqlParameter("@val1", val1),
new SqlParameter("@val2", val2),
new SqlParameter
{
ParameterName = "@retVal",
SqlDbType = SqlDbType.Int,
Direction = ParameterDirection.ReturnValue,
Value = -1
}
};
await context.Database.ExecuteSqlCommandAsync("EXEC @retVal = Example_SP @val1, @val2", parameters);
我正在使用的SP很好,并在SQL MS中返回一个值。但是当我使用EF执行它时,我被告知我必须声明标量变量@retVal'。这不是我的SqlParameter吗??
我尝试删除参数中的'@'符号,正如有些人在其他地方建议的那样,但据我所知,'@'符号是可选的,无论如何都没有区别。
如何使用ExecuteSqlCommandAsync从SP获取返回值而不会导致错误?
谢谢!
答案 0 :(得分:7)
您需要使用ParameterDirection.Output
代替ParameterDirection.ReturnValue
。
答案 1 :(得分:0)
TL&DR::创建一个输出变量以容纳“返回” ID查询。该答案与POSTGRES数据库有关。
比率:ExecuteSqlCommand函数返回受影响的行数。您需要一个附加输出,即插入的“返回”标识。因此,您需要提供一个OUTPUT参数,该参数可以保存该值。
解决方案
在NpgsqlParameter中使用System.Data.ParameterDirection.Output作为Direction。
NpgsqlParameter idOut = new NpgsqlParameter
{
Direction = System.Data.ParameterDirection.Output
};
await _context.Database.ExecuteSqlCommandAsync($"INSERT INTO mytable (myid, create_time, modified_time) VALUES ({mytableid}, now(),now()) RETURNING myid;", idOut);
答案 2 :(得分:-1)
让我介绍一些扩展的解决方案。
在这个工作示例中,如您所见,有一个存储过程,它具有输出参数和 async/await C# 代码,显示了如何接受此输出单个值。
我希望它有助于理解该方法,并且将是对您问题的扩展答案。
C# WinForm
private async void Button1_Click(object sender, EventArgs e)
{
await CleanDevicePositions();
}
public async Task CleanDevicePositions()
{
try
{
var endDate = DateTime.Now.AddDays(-120).ToString("yyyyMMdd");
using (var db = new TechServiceEntities())
{
db.Database.CommandTimeout = 18000;
object[] parameters = {
new SqlParameter("@MaxToDeleteInp", 1000),
new SqlParameter("@BatchSizeInp", 100),
new SqlParameter("@ToEndDateInp", endDate),
new SqlParameter
{
ParameterName = "@Records",
SqlDbType = SqlDbType.Int,
Value = -1,
Direction = ParameterDirection.Output
}
};
// For result description have a look at https://docs.microsoft.com/en-us/sql/relational-databases/stored-procedures/return-data-from-a-stored-procedure?view=sql-server-ver15
var result = await db.Database.
ExecuteSqlCommandAsync($"EXEC dbo.DeleteOldDevicePositions @MaxToDeleteInp, @BatchSizeInp, @ToEndDateInp, @Records OUTPUT", parameters)
.ConfigureAwait(false); ;
var retValue = (parameters[3] as SqlParameter).Value;
BeginInvokeLambda(listBox1, () => { listBox1.Items.Add(retValue); });
BeginInvokeLambda(listBox1, () => { listBox1.Items.Add(result); });
}
}
catch (SqlException ex)
{
Debug.WriteLine(ex);
BeginInvokeLambda(listBox1, () => { listBox1.Items.Add(ex.ToString()); });
BeginInvokeLambda(listBox1, () => { listBox1.Items.Add(ex.Message); });
}
catch (Exception ex)
{
Debug.WriteLine(ex);
BeginInvokeLambda(listBox1, () => { listBox1.Items.Add(ex.ToString()); });
BeginInvokeLambda(listBox1, () => { listBox1.Items.Add(ex.Message); });
}
}
private static IAsyncResult BeginInvokeLambda(Control control, Action action)
{
if (!control.IsDisposed)
{
return control.BeginInvoke(action, null);
}
return null;
}
TSQL 这个 SP 只是按照其数量的定义批量大小删除一些记录。
ALTER PROCEDURE [dbo].[DeleteOldDevicePositions]
@MaxToDeleteInp int = 1000, -- Max amount of records to delete
@BatchSizeInp int = 100, -- it is
@ToEndDateInp datetime, -- Delete until this datetime
@Records int OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DECLARE @MaxToDelete int , @BatchSize int , @ToEndDate datetime;
SET @MaxToDelete = @MaxToDeleteInp;
SET @BatchSize = @BatchSizeInp;
SET @ToEndDate = @ToEndDateInp;
DECLARE @TransactionInterval tinyint = 5, @FactDeleted int = 0;
DECLARE @counter int = 1;
DECLARE @s datetime, @e datetime, @r int = 1;
SELECT @s = MIN(AtTime) FROM dbo.DevicePositions;
BEGIN TRANSACTION;
WHILE (@r > 0)
BEGIN
IF @r % @TransactionInterval = 1
BEGIN
COMMIT TRANSACTION;
BEGIN TRANSACTION;
END
DELETE TOP (@BatchSize) FROM DevicePositions WHERE AtTime >= @s AND AtTime <= @ToEndDate;
SET @FactDeleted = @FactDeleted +@BatchSize;
SET @r = @@ROWCOUNT;
SET @counter = @counter + 1;
IF @FactDeleted >= @MaxToDelete
BREAK;
END
IF @@TRANCOUNT > 0
BEGIN
COMMIT TRANSACTION;
IF @counter % 10 = 0 -- or maybe 100 or 1000
BEGIN
CHECKPOINT;
END
END
SELECT @Records = A.Records FROM (
SELECT OBJECT_NAME(object_id) as ID, SUM(row_count) AS Records FROM sys.dm_db_partition_stats WHERE
object_id = OBJECT_ID('DevicePositions') AND index_id < 2
GROUP BY OBJECT_NAME(object_id) ) A
RETURN @Records;
END
应用 在这里可以看到删除后的记录数。