Это, казалось бы, запутанное поведение можно прояснить, добавив регистрацию ошибок в ваши блоки catch
.Следующая модификация вашего тестового кода добавляет регистрацию ошибок (и некоторые другие улучшения), которая показывает, что на самом деле происходит в процессе:
begin try
begin transaction;
INSERT INTO dbo.my_view (attr1, attr2, attr3, attr4) VALUES (1,2,3,4);
SELECT * FROM dbo.entity1;
BEGIN TRY
INSERT INTO dbo.my_view (attr1, attr2, attr3) VALUES (3,2,1);
END TRY
BEGIN CATCH
-- Logging - inner CATCH
select 'Inner', @@trancount, error_number(), error_message(), error_procedure(), error_line();
END CATCH;
select * from dbo.entity1;
rollback;
end try
begin catch
-- Logging - outer CATCH
select 'Outer', @@trancount, error_number(), error_message(), error_procedure(), error_line();
-- Conditional rollback, because some errors always terminate the transaction
if @@trancount > 0
rollback;
end catch;
Если вы запустите этот код с неповрежденным триггером, вы увидите ошибкуперехвачен внутренним CATCH
:
3931
Текущая транзакция не может быть зафиксирована и не может быть откатлена до точки сохранения.Откатите всю транзакцию.
Поиск по номеру ошибки приводит к этой записи с похожим вопросом.В своем ответе Руцки показывает, что виновником такого поведения является опция сеанса XACT_ABORT
, которая, по-видимому, установлена на ON
для триггеров по умолчанию.Если вы намереваетесь следовать архитектуре на основе триггера, то отключение этой опции в вашем триггере поможет:
create or alter trigger dbo.tg_my_view_ins
on dbo.my_view
instead of insert as
-- Implicitly set to ON in triggers by default; makes error handling impossible
set xact_abort off;
begin try
save transaction here;
INSERT INTO dbo.entity1 (attr1, attr2)
SELECT attr1, attr2 FROM inserted;
INSERT INTO dbo.entity2 (entity1_id, attr3, attr4)
SELECT e.id, attr3, attr4
FROM inserted i
-- The actual JOIN condidions should reference a natural key in the master table.
-- This is just an example.
inner join dbo.entity1 e on e.attr1 = i.attr1 and e.attr2 = i.attr2;
end try
begin catch
if @@trancount > 0
rollback transaction here;
end catch;
return;
GO
(Опять же, я исправил несколько других проблем с вашим кодом.)