【问题标题】:EF6 - ExecuteSqlCommandAsync - Get return parameter (declare scalar variable error)EF6 - ExecuteSqlCommandAsync - 获取返回参数(声明标量变量错误)
【发布时间】:2014-11-08 23:16:02
【问题描述】:

我有以下代码:

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

谢谢!

【问题讨论】:

  • 在您的示例中,@retVal 不是返回值,只是一个输出参数,恰好被分配了 SP 的返回值。

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


【解决方案1】:

您需要使用ParameterDirection.Output 而不是ParameterDirection.ReturnValue

【讨论】:

    【解决方案2】:

    TL&DR:创建一个输出变量来容纳“返回”ID 查询。这个答案与 POSTGRES 数据库有关。

    基本原理:ExecuteSqlCommand 函数返回受影响的行数。您需要一个额外的输出,即插入的“返回”ID。因此,您需要提供一个 OUTPUT 参数,该参数可以保存该值。

    解决方案

    使用 System.Data.ParameterDirection.Output 作为 NpgsqlParameter 中的方向。

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

    【讨论】:

      【解决方案3】:

      让我介绍一些扩展的解决方案。

      在这个工作示例中,如您所见,有一个存储过程,它具有输出参数和异步/等待 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
      

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

      【讨论】:

      • 这个问题所需要的一切都在一个简单、尖锐、被接受的答案中表达出来。我不知道这段无法解释的代码增加了什么。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-03-07
      • 1970-01-01
      • 2019-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多