2011-03-03 55 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. 构建你自己的轻质异步日志系统(使用同步队列)