TransactionScope + таблица аудита + фиксация транзакции в хранимой процедуре - PullRequest
0 голосов
/ 04 марта 2011

Моя компания использует SQL Server 2008. У меня возникла таблица аудита с проблемами транзакций.

У меня есть хранимая процедура.

create proc MySP
as begin
    insert into MY_TABLE values('Value1');

    begin transaction TX_MY_SP
    -- write to audit table permanently
    insert into AUDIT_TABLE values('Value1 is inserted.'); 
    commit transaction TX_MY_SP
end

У меня есть блок VB.netкод

Using tx = New TransactionScope()
    Using conn = New SqlConnection(MY_CONN_STR)
        Using cmd = New SqlCommand("MySP", conn)
            conn.Open()
            cmd.ExecuteNonQuery()
            Throw New ApplicationException("Always throw exception.")
        End Using
    End Using
    tx.Complete()
End Using

Однако в AUDIT_TABLE не вставлено ни одной записи.Я нашел причину в MSDN http://msdn.microsoft.com/en-us/library/ms189336.aspx

У меня вопрос, как я могу вставить записи AUDIT_TABLE с хранимой процедурой.

Спасибо!

Ответы [ 3 ]

1 голос
/ 04 января 2012

По сути, вы можете иметь одну асинхронную систему аудита / регистрации.Таким образом, ваш аудит будет выполняться в другом потоке, и не имеет значения, будет ли сбой основной области транзакции или нет.

  1. использование библиотеки предприятия + MSMQ
  2. создание собственной облегченной системы асинхронного журнала (использование очереди синхронизации)
0 голосов
/ 04 марта 2011

Любая операция внутри транзакции, которая откатывается, также откатывается. Невыполнение этого требования нарушит атомарность транзакции. Учитывая, что проверяемая вами деятельность откатывается, весьма вероятно, что вы все равно действительно хотите отменить аудит.

Тем не менее, существуют законные случаи, когда необходимо записывать операции вне области текущей транзакции, например, в определенных случаях отладки. Есть известные обходные пути, например, использование уведомлений о событиях для настраиваемого пользователем класса событий , затем использование sp_trace_generateevent, чтобы вызвать уведомление о событии активированная процедура для запуска и записи аудита. Поскольку события профилировщика генерируются вне области транзакции, запись аудита не откатывается.

:setvar dbname testdb
:on error exit

set nocount on;
use master;

if exists (
    select * from sys.server_event_notifications
    where name = N'audit')
begin
    drop event notification audit on server;
end 
go

if db_id('$(dbname)') is not null
begin
    alter database [$(dbname)] set single_user with rollback immediate;
    drop database [$(dbname)];
end
go

create database [$(dbname)];
go

alter authorization on database::[$(dbname)] to [sa];
go

use [$(dbname)];
go

create queue audit;
create service audit on queue audit (
    [http://schemas.microsoft.com/SQL/Notifications/PostEventNotification]);
go  

create table audit_table (
    Time datetime not null,
    TextData nvarchar(256) not null);
go

create procedure usp_audit
as
begin
declare @h uniqueidentifier, @mt sysname, @mb varbinary(max), @mx xml;
begin transaction;
receive top(1) @h = conversation_handle,
    @mt = message_type_name,
    @mb = message_body
from audit;
if (@mt = N'http://schemas.microsoft.com/SQL/Notifications/EventNotification')
begin
    select @mx = cast(@mb as xml);
    insert into audit_table (Time, TextData)
    values (
        @mx.value(N'(/EVENT_INSTANCE/PostTime)[1]', N'datetime'),
        @mx.value(N'(/EVENT_INSTANCE/TextData)[1]', N'nvarchar(256)'));
end
else if (@mt = N'http://schemas.microsoft.com/SQL/ServiceBroker/Error'
    or @mt = N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog')
begin
    end conversation @h;
end 
commit
end 
go

alter queue audit
    with activation (
        status = on,
        procedure_name = usp_audit,
        max_queue_readers = 1,
        execute as owner);
go      

create event notification audit
on server for USERCONFIGURABLE_0
to service N'audit', N'current database';
go

begin transaction;
exec sp_trace_generateevent 82, N'this was inserted from a rolled back';
rollback
go

waitfor delay '00:00:05';
select * from audit_table;
go
0 голосов
/ 04 марта 2011

Когда вы используете TransactionScope, вам нужно вызвать метод Complete, прежде чем выйти из его области действия, если вы не хотите, чтобы транзакция откатывалась:

Using tx = New TransactionScope()
    Using conn = New SqlConnection(MY_CONN_STR)
        Using cmd = New SqlCommand("MySP", conn)
            conn.Open()
            cmd.ExecuteNonQuery()
            'Throw New ApplicationException("Always throw exception.")
        End Using
    End Using

    tx.Complete() ' <---- Here

End Using
...