обработка ошибок хранимых процедур - PullRequest
0 голосов
/ 28 ноября 2009
i have this procedure for inserting rows in tables(sql server 2005)

CREATE PROCEDURE ans_insert
    (
    @q_desc varchar(2000),
    @sub_id int,
    @marks int,
    @ans1 varchar(1000),
    @ans varchar(1000),
    @userid varchar(15),
    @cr_date datetime

    )
    AS
    BEGIN
    BEGIN TRY
    BEGIN TRANSACTION
        DECLARE @q_id int

        insert into questions(q_desc,sub_id,marks,created_by,DT_created) values(@q_desc,@sub_id,@marks,@userid,@cr_date);
        SET @q_id = IDENT_CURRENT('questions')

        INSERT INTO answers(ans_desc,q_id,created_by,DT_created,istrue)
            VALUES( @ans1,@q_id,@userid,@cr_date,
            CASE WHEN @ans1 =@ans THEN 1 ELSE 0 END);
    COMMIT TRANSACTION

    END TRY
    BEGIN CATCH

        DECLARE @ErrorMessage NVARCHAR(4000);
        DECLARE @ErrorSeverity INT;
        DECLARE @ErrorState INT;
        DECLARE @ErrorLine INT;
        SELECT @ErrorMessage = ERROR_MESSAGE(),
        @ErrorSeverity = ERROR_SEVERITY(),
        @ErrorLine=ERROR_LINE(),
        @ErrorState = ERROR_STATE();
        IF @@TRANCOUNT > 0 


    ROLLBACK TRANSACTION
        RAISERROR (@ErrorMessage,@ErrorSeverity,@ErrorState,@ErrorLine);
    END CATCH
    END

и я называю это из моей формы ASP.NET как

AnsCmd - это моя команда хранимой процедуры ... после добавления всех параметров

               try
                {


                    conn.Open();

                    AnsCmd.ExecuteNonQuery();

                    lblMsg.Visible = true;
                    lblMsg.Text = "success";
                    conn.Close();
                }
                catch (SqlException sqlex)
                {
                    lblMsg.Visible = true;
                    lblMsg.Text = sqlex.ToString();
                }

                catch (Exception ex)
                {
                    lblMsg.Visible = true;
                    lblMsg.Text = ex.ToString();

                }

чтобы проверить, работает ли raiserror, я изменил имя таблицы во вставке с ответов на ответы1, которых не существует ..

при выполнении я получаю сообщение об ошибке как

System.Data.SqlClient.SqlException: недопустимое имя объекта 'answers1'. Число транзакций после EXECUTE указывает на отсутствие оператора COMMIT или ROLLBACK TRANSACTION. Предыдущий счетчик = 0, текущий счетчик = 1. в System.Data.SqlClient.SqlConnection.OnError (исключение SqlException, ......

так эта штука работает нормально или я что-то упустил ???

Ответы [ 2 ]

2 голосов
/ 28 ноября 2009

Пакет (хранимая процедура) прерывается при достижении несуществующей таблицы (которая отложенное разрешение имен ), поэтому ROLLBACK не выполняется.

С MSDN / BOL :

Ошибки компиляции и перекомпиляции на уровне операторов

Существует два типа ошибок, которые не будет обработан TRY… CATCH, если ошибка возникает в том же исполнении уровень как конструкция TRY… CATCH:

  • Ошибки компиляции, такие как синтаксические ошибки, которые мешают пакету выполнение.
  • Ошибки, возникающие при перекомпиляции на уровне операторов, такие как ошибки разрешения имен объектов, которые произойдет после компиляции из-за отложенное разрешение имен.

Когда партия, хранимая процедура или триггер, который содержит TRY ... CATCH конструкция генерирует один из них ошибки, конструкция TRY… CATCH делает не обрабатывать эти ошибки.

Я предлагаю вам добавить SET XACT_ABORT ON вверху. Это заставит ROLLBACK при ошибке и "привести в порядок".

Еще одна вещь ...

SET @q_id = IDENT_CURRENT('questions')

должно быть

SET @q_id = SCOPE_IDENTITY()

Edit:

CREATE PROCEDURE ans_insert
    @q_desc varchar(2000),
    @sub_id int,
    @marks int,
    @ans1 varchar(1000),
    @ans varchar(1000),
    @userid varchar(15),
    @cr_date datetime
AS
BEGIN

SET NOCOUNT, XACT_ABORT ON; -- what I do

BEGIN TRY
....
0 голосов
/ 28 ноября 2009

Не думаю, что это повлияет на исключение - но некоторые мысли:

  • будет SCOPE_IDENTITY() легче (и надежнее), чем IDENT_CURRENT? IDENT_CURRENT может вернуть идентификатор из другого сеанса во время параллельных операций. Также избегайте @@IDENTITY, на который могут повлиять триггеры, выполняющие INSERT s
  • почему бы не позволить вызывающему (.NET) коду беспокоиться о транзакции? намного проще (и более универсально) управлять на более высоком уровне, либо по соединению (SqlTransaction), либо шире (TransactionScope)

Пример SqlTransaction подход:

using(SqlTransaction tran = conn.BeginTransaction()) {
    try {
        // operations (may need to set command.Transaction = tran)
        tran.Commit();
    } catch {
        tran.Rollback();
        throw;
    }
}

Пример TransactionScope подход (** ОБЯЗАТЕЛЬНО РАЗМЕЩАЕТ соединение **)

using(TransactionScope tran = new TransactionScope()) {
    // operations: note no other changes
    tran.Complete();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...