EF6 - ExecuteSqlCommandAsync - 获取返回参数(声明标量变量错误)

时间:2014-11-08 23:16:03

标签: c# sql-server entity-framework-6

我有以下代码:

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获取返回值而不会导致错误?

谢谢!

3 个答案:

答案 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

应用 在这里可以看到删除后的记录数。

enter image description here