【问题标题】:How can I get an error message that happens when using ExecuteNonQuery()?如何在使用 ExecuteNonQuery() 时收到错误消息?
【发布时间】:2025-12-31 15:55:07
【问题描述】:

我正在以这种方式执行命令:

var Command = new SqlCommand(cmdText, Connection, tr);

Command.ExecuteNonQuery();

在命令中有一个错误,但是 .NET 不会抛出任何错误消息。我怎么知道命令没有正确执行,以及如何获取异常?

【问题讨论】:

  • 没有NonQuery 方法 - 如果您使用实际名称真的会更清楚...编辑...
  • @Marc 也许是他自己的自定义方法?
  • @Shadow 那么我们不可能希望调试它
  • @Marc 好吧,希望你是对的,你的答案听起来是正确的。

标签: c# sql ado.net sqlcommand


【解决方案1】:

.NET 确实会引发错误消息...如果严重性为 16 或更高(因为它会引发异常) - 消息将在异常 .Message 中。如果您使用的是具有较低严重性的RAISERROR(或使用PRINT),那么您必须订阅InfoMessage event on the connection

【讨论】:

  • 等等,SQL Server 实际上会使用ExecuteNonQuery() 使用TDS(并因此看到错误),但ExecuteScalar() 不会使用它?我希望ExecuteNonQuery() 根本不消耗任何东西,尽管我实际上更喜欢这样做。
  • @NelsonRothermel 我希望这两个都会导致错误,只要严重性足够高。但是,在使用 ExecuteReader 时有一个极端情况:如果(例如)您有多个选择查询,并且在(例如)第 3 和第 4 个网格之间引发错误,但您通过第一个网格中途处置阅读器:那么错误可能会被遗漏。
  • 我知道ExecuteReader 的问题。但是,我的研究(*.com/questions/1010383/…*.com/a/2400019/177333sqlservercentral.com/Forums/Topic1070081-392-1.aspx#bm1070131)表明,如果在第一个结果集之后,ExecuteScalar 也不会发现错误。我想最好的方法就是尝试一下。
  • 另外,根据我的测试,您不需要多个结果集即可错过错误。一个简单的select ...; raiserror(...) 需要您执行DataReader.NextResult() 才能发现错误。
  • @NelsonRothermel 啊,对 - 在您的 ExecuteScalar 讨论中,您没有提到多个网格。我假设(可能是错误的)如果您使用的是 ExecuteScalar,那么您只进行了一次选择,并且它是在查询的末尾(因为显然,如果您只做一件事,为什么它会在那之后抛出?)
【解决方案2】:

试试下面的。

PS:仅仅因为您使用事务,并不意味着您可以忽略处理异常和回滚。

 public static void MessageEventHandler( object sender, SqlInfoMessageEventArgs e ) {
         foreach( SqlError error in e.Errors ) {
            Console.WriteLine("problem with sql: "+error);
            throw new Exception("problem with sql: "+error);
         }
      }
      public static int executeSQLUpdate(string database, string command) {
         SqlConnection connection = null;
         SqlCommand sqlcommand = null;
         int rows = -1;
         try {
            connection = getConnection(database);
            connection.InfoMessage += new SqlInfoMessageEventHandler( MessageEventHandler );
            sqlcommand = connection.CreateCommand();
            sqlcommand.CommandText = command;
            connection.Open();
            rows = sqlcommand.ExecuteNonQuery();
          } catch(Exception e) {
            Console.Write("executeSQLUpdate: problem with command:"+command+"e="+e);
            Console.Out.Flush();
            throw new Exception("executeSQLUpdate: problem with command:"+command,e);
         } finally {
            if(connection != null) { connection.Close(); }
         } 
         return rows;
      }

这是正确的事务处理:

//public static void ExecuteInTransaction(Subtext.Scripting.SqlScriptRunner srScriptRunner)
        public override void ExecuteInTransaction(string strSQL)
        {

            System.Data.Odbc.OdbcTransaction trnTransaction = null;

            try
            {


                System.Threading.Monitor.Enter(m_SqlConnection);
                if (isDataBaseConnectionOpen() == false)
                    OpenSQLConnection();

                trnTransaction = m_SqlConnection.BeginTransaction();

                try
                {
                    /*
                    foreach (Subtext.Scripting.Script scThisScript in srScriptRunner.ScriptCollection)
                    {
                        System.Data.Odbc.OdbcCommand cmd = new System.Data.Odbc.OdbcCommand(scThisScript.ScriptText, m_sqlConnection, trnTransaction);
                        cmd.ExecuteNonQuery();
                    }
                    */

                    // pfff, mono C# compiler problem...
                    // System.Data.Odbc.OdbcCommand cmd = new System.Data.Odbc.OdbcCommand(strSQL, m_SqlConnection, trnTransaction);
                    System.Data.Odbc.OdbcCommand cmd = this.m_SqlConnection.CreateCommand();
                    cmd.CommandText = strSQL;

                    cmd.ExecuteNonQuery();

                    trnTransaction.Commit();
                } // End Try
                catch (System.Data.Odbc.OdbcException exSQLerror)
                {
                    Log(strSQL);
                    Log(exSQLerror.Message);
                    Log(exSQLerror.StackTrace);
                    trnTransaction.Rollback();
                } // End Catch
            } // End Try
            catch (Exception ex)
            {
                Log(strSQL);
                Log(ex.Message);
                Log(ex.StackTrace);
            } // End Catch
            finally
            {
                strSQL = null;
                if(m_SqlConnection.State != System.Data.ConnectionState.Closed)
                    m_SqlConnection.Close();
                System.Threading.Monitor.Exit(m_SqlConnection);
            } // End Finally


        } // End Sub ExecuteInTransaction

【讨论】:

  • 我真的不同意你选择的错误处理; usinglock 在这里更合适;此外,您不会重新抛出,并且在每个级别进行日志记录都是多余的(这就是 .StackTrace 的用途)。那无声无息地失败了-非常糟糕,IMO。为什么要锁定连接?如果您处于两个线程甚至可以看到相同连接的位置,那么您做错了,IMO。如果有的话,在我看来,这就像一个完美的例子,说明如何进行错误处理。
  • 谢谢回复它真的对我有帮助
  • @Quandary - 对于 DAL,这就是存在连接池的原因。通过锁定,您实际上将整个应用程序限制为一个有用的工作人员 - 这真的非常糟糕 IMO。在一个繁忙的站点(例如,这个站点)上,我们有大量 的并行数据访问。如果我使用单一连接,杰夫会开枪打死我……
  • @Marc Gravell:你的日志永远不够。如果您使用 asp.net 并希望在每个页面上实例化 DAL 类,那将是可怕的。更不用说每次访问 web.config 读取连接字符串信息时的速度损失。正如您在注释掉的部分中看到的那样,这些方法是静态的。由于Flash、AJAX等技术是异步的,所以需要加锁……另外,VB.NET没有加锁,所以需要监控,这是一种自动转码。但是,是的,锁定会更好。 ;) PS:重新抛出被删除,不向用户展示 YSOD。
  • @Quandry - @Quandry - conn.Open(); 存在 - 实际上,在内部我们使用 EnsureOpen() entension-method,它使用 IDisposable 并打开它如果未打开并关闭它在处置(如果它打开它) - 方便的小包装实用程序方法。整个“我们重复使用连接多少”与我试图说明的要点无关 - 所以我没有关注那个
【解决方案3】:

如果您的错误的严重性为 16 或更高,您只会在 C# 中获得异常。如果您使用的是 PRINT,则在 .NET 中不会出现异常。

如果您可以编辑 raise 错误代码,这将导致 C# 中的 SqlException:

RAISERROR('Some error message', 16, 1)

然后您可以访问 SqlException.Errors 集合中的每个单独的错误。

附带说明 - 如果您之后不直接 RETURN,SQL Server 将继续在 RAISERROR 之后运行命令。如果不返回,可能会返回多个错误。

【讨论】:

    【解决方案4】:

    在 ExecuteNonQuery 中只会抛出严重性较高的错误。我使用 OdbcCommand.ExecuteNonQuery() 方法观察到了另一种情况。可能这对于 SqlCommand.ExecuteNonQuery() 也是如此。如果 CommandText 属性中包含的 SQL 是单个语句(例如:INSERT INTO table (col1,col2) VALUES (2,'ABC'); )并且上述语句中存在外键违规或主键违规 ExecuteNonQuery会抛出异常。但是,如果您的 CommandText 是一个批处理,其中您有多个用分号分隔的 SQL 语句(如几个 INSERTS 或 UPDATES),并且如果其中一个失败,则 ExecuteNonQuery 不会抛出异常。您需要明确检查该方法返回的受影响记录数。简单地将代码放在 try{}Catch{} 中不会有帮助。

    【讨论】:

      【解决方案5】:

      您使用 try/catch 捕获 SqlException

       try
        {
             //.......
          Command.ExecuteNonQuery();      
         }
          catch (SqlException ex)
           {   
             log (SqlExceptionMessage(ex).ToString());
           }
      

      以下方法捕获可以记录或显示给用户的 SqlException 详细信息

        public StringBuilder SqlExceptionMessage(SqlException ex)
          {
              StringBuilder sqlErrorMessages = new StringBuilder("Sql Exception:\n");
      
              foreach (SqlError error in ex.Errors)
              {
                  sqlErrorMessages.AppendFormat("Mesage: {0}\n", error.Message)
                      .AppendFormat("Severity level: {0}\n", error.Class)
                      .AppendFormat("State: {0}\n", error.State)
                      .AppendFormat("Number: {0}\n", error.Number)
                      .AppendFormat("Procedure: {0}\n", error.Procedure)
                      .AppendFormat("Source: {0}\n", error.Source)
                      .AppendFormat("LineNumber: {0}\n", error.LineNumber)
                      .AppendFormat("Server: {0}\n", error.Server)
                      .AppendLine(new string('-',error.Message.Length+7));
      
              }
              return sqlErrorMessages;
          }
      

      生成的消息如下所示:

       Sql Exception:
       Mesage: Error converting data type nvarchar to datetime.
       Severity level: 16
       State: 5
       Number: 8114
       Procedure: Sales by Year
       Source: .Net SqlClient Data Provider
       LineNumber: 0
       Server: myserver
       -------------------------------------------------------
      

      【讨论】:

        【解决方案6】:

        受 M Hassan、Stefan Steiger 和 Mark Gravell 在此线程中的工作的启发,以下是此处发生的最小概念验证示例:

        private static void DoSql()
        {
            // Errors of severity level of 10 or less 
            // will NOT bubble up to .Net as an Exception to be caught in the usual way
            const string sql = @"RAISERROR('A test error message of low severity', 10, 1)";
        
            using (SqlConnection conn = new SqlConnection(myConnString))
            {
                conn.Open();
        
                // Hook up my listener to the connection message generator
                conn.InfoMessage += new SqlInfoMessageEventHandler(MySqlMessageHandler);
        
                using (SqlCommand cmd = new SqlCommand(sql, conn))
                {
                    cmd.ExecuteNonQuery();
                    // code happily carries on to this point
                    // despite the sql Level 10 error that happened above
                }
            }
        }
        
        
        private static void MySqlMessageHandler(object sender, SqlInfoMessageEventArgs e)
        {
            // This gets all the messages generated during the execution of the SQL, 
            // including low-severity error messages.
            foreach (SqlError err in e.Errors)
            {
                // TODO: Something smarter than this for handling the messages
                MessageBox.Show(err.Message);
            }
        }
        

        【讨论】:

          【解决方案7】:

          我发现这在使用 Oracle ODP.Net 的 WCF 服务中非常适合我 -

                      try
                      {
                          cmd.Connection = conn;
                          conn.Open();
                          cmd.ExecuteNonQuery();
                      }
                      catch (OracleException oex)
                      {
                          string errmsg = oex.Message;
                          Logger.Instance.WriteLog(@"Some error --> " + errmsg);
                          throw new Exception(errmsg);
                      }
                      catch (Exception ex)
                      {
                          throw ex;
                      }
                      finally
                      {
                          cleanup...
                      }
          

          【讨论】:

            【解决方案8】:

            这个代码当命令为finsth时,结合try cath,返回错误:

            SqlCommand.EndExecuteNonQuery(result) 
            

            这是我的完整课程代码:

            Imports System.Data.SqlClient
            Imports System.DirectoryServices.ActiveDirectory
            
            Class clsExecuteAsync
            
            
                Public Event EnProceso(Identificador As Integer, Mensaje As String)
                Public Event Finalizado(IDentificador As Integer, Mensaje As String)
                Public Event CancelarProcesoEnEjecucion(Identificador As Integer, ByRef Cancel As Boolean)
            
            
            
                Dim Cancelar As Boolean
            
                Sub CancelarProceso()
                    Cancelar = True
                End Sub
            
            
                Function test() As Boolean
                    ' This is a simple example that demonstrates the usage of the
                    ' BeginExecuteNonQuery functionality.
                    ' The WAITFOR statement simply adds enough time to prove the
                    ' asynchronous nature of the command.
                    Dim commandText As String = "UPDATE Production.Product SET ReorderPoint = ReorderPoint + 1 " & "WHERE ReorderPoint Is Not Null;" & "WAITFOR DELAY '0:0:3';" & "UPDATE Production.Product SET ReorderPoint = ReorderPoint - 1 " & "WHERE ReorderPoint Is Not Null"
                    Return (RunCommandAsynchronously(0, commandText, GetConnectionString()))
                    Console.WriteLine("Press ENTER to continue.")
                    Console.ReadLine()
            
                End Function
            
                Function ExecuteAsync(Identificador As Integer, Sql As String, Optional CadenaConexion As String = "") As String
                    If CadenaConexion = "" Then
                        CadenaConexion = clsIni.LeeIni("Provider")
                    End If
            
                    Return RunCommandAsynchronously(Identificador, Sql, CadenaConexion)
            
                End Function
            
                Function RunCommandAsynchronously(Identificador As Integer, commandText As String, connectionString As String) As String
                    ' Given command text and connection string, asynchronously execute
                    ' the specified command against the connection. For this example,
                    ' the code displays an indicator as it is working, verifying the
                    ' asynchronous behavior.
                    Dim Resultado As String = ""
                    Try
            
                        Dim connection As SqlConnection
                        Dim SqlCommand As SqlCommand
            
                        connection = New SqlConnection(connectionString)
                        Dim count As Integer = 0
            
                        'testint to catch the error, but not run for me
                        AddHandler connection.InfoMessage, AddressOf ErrorEnConexion
            
            
                        SqlCommand = New SqlCommand(commandText, connection)
                        connection.Open()
                        Dim result As IAsyncResult = SqlCommand.BeginExecuteNonQuery()
                        While Not result.IsCompleted
                            Console.WriteLine("Waiting ({0})", count = count + 1)
                            ' Wait for 1/10 second, so the counter
                            ' does not consume all available resources
                            ' on the main thread.
                            System.Threading.Thread.Sleep(100)
                            RaiseEvent EnProceso(Identificador, commandText)
                            Application.DoEvents()
            
                            If Cancelar Then
                                Cancelar = False
            
                                'cancelar 
                                Dim Cancel As Boolean = False
                                RaiseEvent CancelarProcesoEnEjecucion(Identificador, Cancel)
            
                                If Cancel = False Then
                                    Resultado = "Cancelado"
                                    GoTo SALIR
                                End If
            
                            End If
            
                        End While
            
                        'Console.WriteLine("Command complete. Affected {0} rows.", Command.EndExecuteNonQuery(Result))
            
                        '   MsgBox("El comando se ejecutó. " & SqlCommand.EndExecuteNonQuery(result), MsgBoxStyle.Information)
            
                        'detect error: this code lunch and error: Cath with try cacth code
                        SqlCommand.EndExecuteNonQuery(result)
            
                        RaiseEvent Finalizado(Identificador, SqlCommand.EndExecuteNonQuery(result))
            
                        Resultado = "OK"
            
                    Catch ex As SqlException
                        Console.WriteLine("Error ({0}): {1}", ex.Number, ex.Message)
                        Resultado = ex.Message
            
            
                    Catch ex As InvalidOperationException
                        Console.WriteLine("Error: {0}", ex.Message)
                        Resultado = ex.Message
                    Catch ex As Exception
                        ' You might want to pass these errors
                        ' back out to the caller.
                        Console.WriteLine("Error: {0}", ex.Message)
                        Resultado = ex.Message
                    End Try
            
            
            SALIR:
                    Return Resultado
            
                End Function
            
                Private Sub ErrorEnConexion(sender As Object, e As SqlInfoMessageEventArgs)
                    MsgBox(e.Message)
            
                End Sub
            
            
                Private Function GetConnectionString() As String
                    ' To avoid storing the connection string in your code,
                    ' you can retrieve it from a configuration file.
                    ' If you have not included "Asynchronous Processing=true" in the
                    ' connection string, the command is not able
                    ' to execute asynchronously.
                    Return "Data Source=(local);Integrated Security=SSPI;" & "Initial Catalog=AdventureWorks; Asynchronous Processing=true"
            
                End Function
            
            
            End Class
            

            【讨论】:

              最近更新 更多