2011-03-03 57 views
0

我公司使用SQL Server 2008中,我面臨着交易問題審計表。的TransactionScope +審覈表+提交事務存儲過程中

我有一個存儲過程。

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。

謝謝!

+0

對不起,我的VB.net代碼。我錯過了'tx.Complete()'語句。但是,它永遠不會去那條線。因爲我總是在'cmd.ExecuteNonQuery()'方法後拋出異常。我想要的是回滾插入MY_TABLE中的記錄,但是** NOT **回滾插入AUDIT_TABLE中的記錄。 – 2011-03-04 00:39:01

回答

0

當您使用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 
+0

我只想MY_TABLE回滾但不是AUDIT_TABLE。我知道'tx.Complete()'方法。但是,因爲我有'拋出新的ApplicationException(「總是拋出異常。」)'行,沒有辦法''tx.Complete()'。 – 2011-03-04 00:34:03

+0

@Alex - 如果您在交易中註冊了兩個呼叫,則兩者都將回滾,或者兩者都將被提交。如果您不想要這種行爲,請不要在一次交易中註冊這兩個電話。 – Oded 2011-03-04 08:48:55

0

一個事務中的任何操作是回滾回滾以及。不這樣做會破壞事務的原子性。鑑於您正在審計的活動正在回滾,很可能您確實希望無論如何都要回滾審計。

儘管如此,也有合法的情況下,當一個人需要有記錄當前事務,如某些調試案範圍以外的業務。有已知的解決方法,如使用event notificationsuser configurable事件類,然後使用sp_trace_generateevent導致事件通知activated procedure運行並記錄審計。由於事件探查器事件是在事務作用域之外生成的,因此審覈記錄不會回滾。

: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 
1

基本上,你可以做的是有一個異步審計/日誌系統。 所以你的審計將在不同的線程中運行,並且它不會不管你交易主體範圍是否失敗。

  1. 利用企業庫+ MSMQ
  2. 構建你自己的輕質異步日誌系統(使用同步隊列)