Как получить сообщение об ошибке, которое происходит при использовании ExecuteNonQuery()?

Я выполняю команду таким образом:

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

Command.ExecuteNonQuery();

в команде есть ошибка, однако .NET не выдает никакого сообщения об ошибке. Как я мог знать, что команда не выполнена должным образом, и как получить исключение?

6 ответов


вы получите исключение только в C#, если серьезность вашей ошибки составляет 16 или выше. Если вы используете печать, вы не получите исключения .Сеть.

Если вы можете редактировать код ошибки raise, это вызовет SqlException в C#:

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

затем вы можете добраться до каждой отдельной ошибки в SqlException.Коллекция ошибок.

просто Примечание-SQL Server будет продолжать выполнять команды после RAISERROR, если не RETURN непосредственно после этого. Если вы не возвращайтесь, вы можете получить несколько ошибок обратно.


.NET действительно вызывает сообщение об ошибке... если серьезность 16 или выше (так как она вызывает исключение) - сообщение будет в исключении .Message. Если вы используете RAISERROR с меньшей серьезностью (или с использованием PRINT), то вам придется подписаться на InfoMessage событие при подключении.


в ExecuteNonQuery будут возвращены только ошибки высокой степени серьезности. Есть еще один сценарий, который я наблюдал с OdbcCommand.Метод ExecuteNonQuery (). Может быть, это верно для SqlCommand.ExecuteNonQuery () также. Если SQL, содержащийся в свойстве CommandText, является одним оператором (например: INSERT INTO table (col1,col2) VALUES (2,'ABC'); ) и если есть нарушение внешнего ключа или нарушение первичного ключа в приведенном выше операторе, ExecuteNonQuery выдаст исключение. Однако, если ваш CommandText-это пакет, в котором у вас есть несколько операторов SQL, разделенных точкой с запятой (например, несколько вставок или обновлений), и если один из них терпит неудачу, ExecuteNonQuery не возвращает исключение. Необходимо явно проверить количество записей, затронутых, возвращаемых методом. Просто положить код в try{}Catch{} не поможет.


попробуйте ниже.

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

вы ловите SqlException, используя try / catch

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

следующий метод улавливает детали 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
 -------------------------------------------------------

вдохновленный работой м Хассана, Штефана Штайгера и Марка Гравелла в этой теме, вот минимальный пример доказательства концепции того, что здесь происходит:

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