Произошло масштабное обновление обработки ошибок в SQL Server 2005. Эти статьи довольно обширны: Обработка ошибок в SQL 2005 и более поздних версиях Erland Sommarskog и Обработка ошибок в SQL 2000 - a Фон Эрланда Соммарского
Лучший способ - что-то вроде этого:
Создайте свою хранимую процедуру как:
CREATE PROCEDURE YourProcedure
AS
BEGIN TRY
BEGIN TRANSACTION --SqlTransaction
DECLARE @ReturnValue int
SET @ReturnValue=NULL
IF (DAY(GETDATE())=1 --logical error
BEGIN
SET @ReturnValue=5
RAISERROR('Error, first day of the month!',16,1) --send control to the BEGIN CATCH block
END
SELECT 1/0 --actual hard error
COMMIT TRANSACTION --SqlTransaction
RETURN 0
END TRY
BEGIN CATCH
IF XACT_STATE()!=0
BEGIN
ROLLBACK TRANSACTION --only rollback if a transaction is in progress
END
--will echo back the complete original error message to the caller
--comment out if not needed
DECLARE @ErrorMessage nvarchar(400), @ErrorNumber int, @ErrorSeverity int, @ErrorState int, @ErrorLine int
SELECT @ErrorMessage = N'Error %d, Line %d, Message: '+ERROR_MESSAGE(),@ErrorNumber = ERROR_NUMBER(),@ErrorSeverity = ERROR_SEVERITY(),@ErrorState = ERROR_STATE(),@ErrorLine = ERROR_LINE()
RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorNumber,@ErrorLine)
RETURN ISNULL(@ReturnValue,1)
END CATCH
GO
однако это только для SQL Server 2005 и выше. Без использования блоков TRY-CATCH в SQL Server 2005 вам будет очень сложно удалить все сообщения, которые отправляет SQL Server. extra messages
, на который вы ссылаетесь, вызван природой того, как откаты обрабатываются с помощью @@ trancount:
из http://www.sommarskog.se/error-handling-I.html#trancount
@@ trancount - это глобальная переменная, которая
отражает уровень вложенности
сделки. Каждый НАЧАЛО СДЕЛКИ
увеличивает @@ trancount на 1, и каждый
COMMIT TRANSACTION уменьшается
@@ trancount by 1. На самом деле ничего нет
совершено, пока @@ trancount не достигнет 0.
ROLLBACK TRANSACTION откатывается
все самое внешнее НАЧИНАЕТСЯ
СДЕЛКА (если вы не использовали
довольно экзотическая SAVE TRANSACTION), и
силы @@ transcount до 0, с уважением
предыдущее значение.
При выходе из хранимой процедуры, если
@@ trancount не имеет такой же
значение, как это было при процедуре
началось выполнение, SQL Server повышает
ошибка 266. Эта ошибка не возникает,
хотя, если процедура вызывается
от триггера, напрямую или
косвенно. Не поднимается ли он, если
вы работаете с SET IMPLICIT
СДЕЛКИ ПО
Если вы не хотите получать предупреждение о том, что количество транзакций не совпадает, вам нужно одновременно открывать только одну транзакцию. Вы делаете это, создавая все ваши процедуры так:
CREATE PROC YourProcedure
AS
DECLARE @SelfTransaction char(1)
SET @SelfTransaction='N'
IF @@trancount=0
BEGIN
SET @SelfTransaction='Y'
BEGIN TRANSACTION --SqlTransaction
END
SELECT 1/0
IF @@ERROR<> 0
BEGIN
IF @SelfTransaction='Y'
BEGIN
ROLLBACK TRANSACTION --SqlTransaction
END
RETURN -1
END
ELSE
BEGIN
IF @SelfTransaction='Y'
BEGIN
COMMIT TRANSACTION --SqlTransaction
END
RETURN 0
END
GO
Делая это, вы вводите команды транзакции, только если вы еще не в транзакции. Если вы закодируете все свои процедуры таким образом, только процедура или код C #, который выдает BEGIN TRANSACTION, фактически выдает COMMIT / ROLLBACK, и количество транзакций всегда будет совпадать (вы не получите ошибку).
в C # из Документация класса TransactionScope :
static public int CreateTransactionScope(
string connectString1, string connectString2,
string commandText1, string commandText2)
{
// Initialize the return value to zero and create a StringWriter to display results.
int returnValue = 0;
System.IO.StringWriter writer = new System.IO.StringWriter();
try
{
// Create the TransactionScope to execute the commands, guaranteeing
// that both commands can commit or roll back as a single unit of work.
using (TransactionScope scope = new TransactionScope())
{
using (SqlConnection connection1 = new SqlConnection(connectString1))
{
// Opening the connection automatically enlists it in the
// TransactionScope as a lightweight transaction.
connection1.Open();
// Create the SqlCommand object and execute the first command.
SqlCommand command1 = new SqlCommand(commandText1, connection1);
returnValue = command1.ExecuteNonQuery();
writer.WriteLine("Rows to be affected by command1: {0}", returnValue);
// If you get here, this means that command1 succeeded. By nesting
// the using block for connection2 inside that of connection1, you
// conserve server and network resources as connection2 is opened
// only when there is a chance that the transaction can commit.
using (SqlConnection connection2 = new SqlConnection(connectString2))
{
// The transaction is escalated to a full distributed
// transaction when connection2 is opened.
connection2.Open();
// Execute the second command in the second database.
returnValue = 0;
SqlCommand command2 = new SqlCommand(commandText2, connection2);
returnValue = command2.ExecuteNonQuery();
writer.WriteLine("Rows to be affected by command2: {0}", returnValue);
}
}
// The Complete method commits the transaction. If an exception has been thrown,
// Complete is not called and the transaction is rolled back.
scope.Complete();
}
}
catch (TransactionAbortedException ex)
{
writer.WriteLine("TransactionAbortedException Message: {0}", ex.Message);
}
catch (ApplicationException ex)
{
writer.WriteLine("ApplicationException Message: {0}", ex.Message);
}
// Display messages.
Console.WriteLine(writer.ToString());
return returnValue;
}
Просто мысль, но вы можете использовать TransactionAbortedException
catch для получения фактической ошибки и игнорирования предупреждения о несоответствии количества транзакций.