2013-06-20 34 views
0

我们正在考虑起诉某些SQL CLR触发器来填充特定表的审计日志,我知道如何在服务器中注册CLR程序集和触发器,以及所有当我知道要查找哪些列以及是否必须在审计日志表中插入新记录时工作良好。如何在CLR插入和更新触发器中比较旧字段值和新字段值

现在我们希望这是独立于监测表架构,以便我们不需要编辑和重新部署每增加或重命名到源表的新列触发器,我想坚持在我的审计表一些简单的像包含更改快照ñXML列,是这样的:

<AuditEntry ObjectName='TableName' ObjectId='1'> 
    <Field='Firstname'> 
     <OldValue>David</OldValue> 
     <NewValue>Davide</NewValue> 
    </Field> 
    <Field='Email'> 
     <OldValue/> 
     <NewValue>[email protected]</NewValue> 
    </Field> 
</AuditEntry> 

这个XML仅仅是一个例子,我只需要知道如何写我的触发器C#代码,以便taht它比较旧字段和新字段按字段获取旧值和新值,然后我知道如何将其转储到XML文档中。

非常感谢, Davide。

回答

0

我做了一些测试,我能解决这个我自己,在这里,整个故事的分享它的缘故;-)

1) .NET CLR SQL触发,触发1只,这将是听着两个表,唯一的假设是看着表有一个名为标识

using System.Data.SqlClient; 
using System.Xml; 
using Microsoft.SqlServer.Server; 

namespace Axis.CLR.SampleObjects 
{ 
    using System; 
    using System.Data; 
    using System.Data.SqlTypes; 
    using System.Text; 

    public partial class AuditTrigger 
    { 
     public const string GetTableContextStatement = 
      "SELECT object_name(resource_associated_entity_id) FROM sys.dm_tran_locks WHERE request_session_id = @@spid and resource_type = 'OBJECT'"; 

     [SqlTrigger(Name = "UserNameAudit", Target = "Users", Event = "FOR INSERT")] 
     public static void UserNameAudit() 
     { 
      SqlTriggerContext triggContext = SqlContext.TriggerContext; 

      //SqlPipe sqlP = SqlContext.Pipe; 

      using (SqlConnection conn = new SqlConnection("context connection=true")) 
      using (SqlCommand sqlComm = conn.CreateCommand()) 
      { 
       conn.Open(); 

       // Gets a reference to the affected table name 
       string tableName = string.Empty; 
       using (SqlCommand cmd = new SqlCommand(GetTableContextStatement, conn)) 
       { 
        tableName = cmd.ExecuteScalar().ToString(); 
       } 

       // STORING INSERT AUDIT 

       if (triggContext.TriggerAction == TriggerAction.Insert) 
       { 
        #region handling INSERT action 

        sqlComm.CommandText = "SELECT * from INSERTED"; 
        var reader = sqlComm.ExecuteReader(); 

        if (reader.Read()) 
        { 
         XmlDocument finalDocument = new XmlDocument(); 

         XmlNode rootElement = finalDocument.CreateNode(XmlNodeType.Element, tableName, string.Empty); 

         XmlAttribute newAttribute = finalDocument.CreateAttribute("Id"); 
         newAttribute.Value = reader.GetInt64(reader.GetOrdinal("Id")).ToString(); 
         rootElement.Attributes.Append(newAttribute); 

         newAttribute = finalDocument.CreateAttribute("Operation"); 
         newAttribute.Value = "INSERT"; 
         rootElement.Attributes.Append(newAttribute); 

         finalDocument.AppendChild(rootElement); 

         XmlNode createdElement = finalDocument.CreateNode(XmlNodeType.Element, "Fields", string.Empty); 

         for (int i = 0; i < reader.FieldCount; i++) 
         { 
          XmlNode fieldElement = finalDocument.CreateNode(XmlNodeType.Element, reader.GetName(i), string.Empty); 

          if (reader.IsDBNull(i)) 
          { 
           fieldElement.InnerText = "NULL"; 
          } 
          else 
          { 
           fieldElement.InnerText = reader.GetValue(i).ToString(); 
          } 

          createdElement.AppendChild(fieldElement); 
         } 

         // Node was added 
         rootElement.AppendChild(createdElement); 

         // Adds the Audit 

         sqlComm.CommandText = "[dbo].[AddAuditTrail]"; 
         sqlComm.CommandType = CommandType.StoredProcedure; 

         SqlParameter xmlParamA = new SqlParameter("@ObjectId", SqlDbType.BigInt); 
         xmlParamA.Value = reader.GetInt64(reader.GetOrdinal("Id")); 
         sqlComm.Parameters.Add(xmlParamA); 

         reader.Close(); 

         sqlComm.Parameters.AddWithValue("@ObjectName", tableName); 

         SqlParameter xmlParamB = new SqlParameter("@TraceXML", SqlDbType.Xml); 
         xmlParamB.Value = new SqlXml(new XmlTextReader(finalDocument.OuterXml, XmlNodeType.Document, null)); 
         sqlComm.Parameters.Add(xmlParamB); 

         sqlComm.Parameters.AddWithValue("@AuditType", "INSERT"); 

         sqlComm.ExecuteNonQuery(); 

         //sqlP.Send(string.Format("Generated AFTER INSERT XML is: '{0}'", finalDocument.OuterXml)); 
        } 

        #endregion handling INSERT action 
       } 
       else if (triggContext.TriggerAction == TriggerAction.Update) 
       { 
        #region handling UPDATE action 

        DataSet values = new DataSet(); 
        SqlDataAdapter adapter = new SqlDataAdapter(sqlComm); 

        sqlComm.CommandText = "SELECT * from INSERTED"; 
        adapter.Fill(values, "INSERTED"); 

        sqlComm.CommandText = "SELECT * from DELETED"; 
        adapter.Fill(values, "DELETED"); 

        StringBuilder builder = new StringBuilder(); 

        builder.Append("<Fields>"); 

        int recordId = 0; 

        for (int i = 0; i < values.Tables["INSERTED"].Columns.Count; i++) 
        { 
         string colName = values.Tables["INSERTED"].Columns[i].ColumnName; 

         if (colName.ToLower().Equals("id")) 
         { 
          recordId = Convert.ToInt32(values.Tables["DELETED"].Rows[0][i]); 

          builder.AppendFormat("<Id value='{0}' />", recordId); 
         } 

         // if both nulls or both the same, no audit needed... 

         if (values.Tables["INSERTED"].Rows[0].IsNull(i) && values.Tables["DELETED"].Rows[0].IsNull(i)) 
         { 
          continue; 
         } 

         if (values.Tables["INSERTED"].Rows[0][i].Equals(values.Tables["DELETED"].Rows[0][i])) 
         { 
          continue; 
         } 

         builder.AppendFormat("<{0}>", colName); 

         // DUMPING OLD VALUE 
         builder.Append("<OldValue>"); 

         if (values.Tables["DELETED"].Rows[0].IsNull(i)) 
         { 
          builder.Append("NULL"); 
         } 
         else 
         { 
          builder.Append(values.Tables["DELETED"].Rows[0][i]); 
         } 

         builder.Append("</OldValue>"); 

         // DUMPING NEW VALUE 
         builder.Append("<NewValue>"); 

         if (values.Tables["INSERTED"].Rows[0].IsNull(i)) 
         { 
          builder.Append("NULL"); 
         } 
         else 
         { 
          builder.Append(values.Tables["INSERTED"].Rows[0][i]); 
         } 

         builder.Append("</NewValue>"); 

         builder.AppendFormat("</{0}>", colName); 
        } 

        builder.Append("</Fields>"); 

        builder.Insert(0, string.Format("<{0} Id='{1}' Operation='{2}'>", tableName, recordId, "UPDATE")); 
        builder.AppendFormat("</{0}>", tableName); 

        // Adds the Audit 

        sqlComm.CommandText = "[dbo].[AddAuditTrail]"; 
        sqlComm.CommandType = CommandType.StoredProcedure; 

        SqlParameter xmlParamA = new SqlParameter("@ObjectId", SqlDbType.BigInt); 
        xmlParamA.Value = recordId; 
        sqlComm.Parameters.Add(xmlParamA); 

        sqlComm.Parameters.AddWithValue("@ObjectName", tableName); 

        SqlParameter xmlParamB = new SqlParameter("@TraceXML", SqlDbType.Xml); 
        xmlParamB.Value = new SqlXml(new XmlTextReader(builder.ToString(), XmlNodeType.Document, null)); 
        sqlComm.Parameters.Add(xmlParamB); 

        sqlComm.Parameters.AddWithValue("@AuditType", "UPDATE"); 

        sqlComm.ExecuteNonQuery(); 

        //sqlP.Send(string.Format("Generated AFTER UPDATE XML is: '{0}'", builder.ToString())); 

        #endregion handling UPDATE action 
       } 
      } 
     } 
    } 
} 

2)这里我用来注册SQL Server上触发并将其链接到两个SQL代码标识列不同的表格(用户和产品),只有1个CLR触发,但在SQL服务器的两个触发器作为外部使用CLR一个,每一个为每个表

USE [Axis_Davide] 
GO 

BEGIN TRANSACTION SCRIPT 
--------------------------------- 

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'trAuditTriggerA') AND type in (N'TA')) 
BEGIN 
    DROP TRIGGER [dbo].[trAuditTriggerA] 
     PRINT('Trigger A was removed'); 
END 

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'trAuditTriggerB') AND type in (N'TA')) 
BEGIN 
    DROP TRIGGER [dbo].[trAuditTriggerB] 
     PRINT('Trigger B was removed'); 
END 


IF EXISTS (SELECT * FROM sys.assemblies asms WHERE asms.name = N'Axis.CLR.SampleObjects' and is_user_defined = 1) 
BEGIN 
    DROP ASSEMBLY [Axis.CLR.SampleObjects] 
    PRINT('Assembly was removed'); 
END 

CREATE ASSEMBLY [Axis.CLR.SampleObjects] 
AUTHORIZATION [dbo] 
FROM 'C:\Axis\SQLCLR_Samples\Axis.CLR.SampleObjects.dll' 
WITH PERMISSION_SET = SAFE 
PRINT('Assembly was created'); 

EXEC('CREATE TRIGGER trAuditTriggerA ON [dbo].[Users] AFTER INSERT, UPDATE AS EXTERNAL NAME [Axis.CLR.SampleObjects].[Axis.CLR.SampleObjects.AuditTrigger].[UserNameAudit]') 
PRINT('Trigger A was created'); 

EXEC('CREATE TRIGGER trAuditTriggerB ON [dbo].[Products] AFTER INSERT, UPDATE AS EXTERNAL NAME [Axis.CLR.SampleObjects].[Axis.CLR.SampleObjects.AuditTrigger].[UserNameAudit]') 
PRINT('Trigger B was created'); 

--------------------------------- 
COMMIT TRANSACTION SCRIPT 

3)这里创建语句创建了审计表

CREATE TABLE [dbo].[AuditTrail] 
(
    [Id] [bigint] IDENTITY(1,1) NOT NULL, 
    [AuditDate] [datetime2](7) NOT NULL, 
    [UserName] [nvarchar](64) NOT NULL, 
    [ObjectId] [bigint] NOT NULL, 
    [ObjectName] [nvarchar](128) NOT NULL, 
    [TraceXML] [xml] NOT NULL, 
    [TraceSize] [int] NOT NULL, 
    [AuditType] [nvarchar](16) NOT NULL, 
CONSTRAINT [PK_AuditTrail] PRIMARY KEY CLUSTERED ([Id] ASC) 
) 
GO 

ALTER TABLE [dbo].[AuditTrail] ADD CONSTRAINT [DF_AuditTrail_AuditDate] DEFAULT (sysutcdatetime()) FOR [AuditDate] 
GO 

ALTER TABLE [dbo].[AuditTrail] ADD CONSTRAINT [DF_AuditTrail_UserName] DEFAULT (suser_sname()) FOR [UserName] 
GO 

4)这里由触发器调用添加在每个插入一个新的审计记录的存储过程/更新

CREATE PROCEDURE [dbo].[AddAuditTrail] 
    @ObjectId bigint, 
    @ObjectName nvarchar(128), 
    @TraceXML xml, 
    @AuditType nvarchar(16) 
AS 
BEGIN 
    -- SET NOCOUNT ON added to prevent extra result sets from 
    -- interfering with SELECT statements. 
    SET NOCOUNT ON; 

    INSERT INTO [dbo].[AuditTrail] ([ObjectId], [ObjectName], [TraceXML], [TraceSize], [AuditType]) 
     VALUES (@ObjectId, @ObjectName, @TraceXML, DATALENGTH(@TraceXML), @AuditType) 
END 
GO 

5)我审核表的内容如下所示,对于INSERT和一个UPDATE

<Users Id="51" Operation="INSERT"> 
    <Fields> 
    <UserName>Davide</UserName> 
    <Pass>Test</Pass> 
    <Id>51</Id> 
    <Email>NULL</Email> 
    </Fields> 
</Users> 

<Users Id="51" Operation="UPDATE"> 
    <Fields> 
    <Id value="51" /> 
    <Email> 
     <OldValue>NULL</OldValue> 
     <NewValue>@</NewValue> 
    </Email> 
    </Fields> 
</Users> 
相关问题