解析大型.NET ERP系統 數據審計功能

數據審計,英語表達是Audit,是追蹤數據變化的過程,記錄數據變化先後的值,供參考分析。經過設置,ERP能夠追蹤一個表的全部字段的變化,也能夠只記錄指定的字段的值變化。歐美企業每一年都有獨立的審計部門,從總經理到下層部門員工,逐個審查過去發生的經濟業務的賬面數據與實際是否一致。ERP中的審計功能,一般會記錄下一個表字段的值的變化。ERP系統經過LLBL Gen Pro ORM框架作數據訪問層,先了解ORM提供的數據審計功能。html

審計功能的兩個重要部分:記錄的變化以及致使變化的動做,持久化變化的數據。數據庫

可用於審計的功做:框架

1 設置屬性值。與框架的一致,改變屬性的值會引起OnChanged事件,可是經過設置CurrentValue的值則不會審計。ide

2 移除對象引用。好比銷售單實例再也不引用客戶實體,從客戶集合(EntityCollection)中刪除客戶實體。性能

3 增長對象引用  與移除對象引用的狀況相反,表示對象的屬性引用到另外一個對象或是實體增長到對象集合中。this

4 保存實體  調用SaveEntity方法。spa

5 更新實體 調用SaveEntity方法。設計

6 刪除實體 調用方法DeleteEntitycode

7  獲取屬性值 獲取屬性值或調用GetCurrentFieldValue方法取值,可是經過獲取CurrentValue的值則不會審計。htm

8  加載實體 調用方法FetchEntity或FetchEntityCollectionNonGeneric。

 

定義一個枚舉上面說到的八種狀況:

public enum AuditType
{
        DeleteOfEntity=1,
        DirectDeleteOfEntities,
        DirectUpdateOfEntities,
        DereferenceOfRelatedEntity,
        ReferenceOfRelatedEntity,
        EntityFieldSet,
        InsertOfNewEntity,
        UpdateOfExistingEntity
}

實體基類EntityBase2已經定義以八種事件中的基礎功能,一一列舉以下:

1 OnAuditEntityFieldSet 設置屬性值

2 OnAuditDereferenceOfRelatedEntity移除對象引用

3 OnAuditReferenceOfRelatedEntity 增長對象引用

4 OnAuditInsertOfNewEntity保存實體

OnAuditUpdateOfExistingEntity更新實體

5 OnAuditDirectUpdateOfEntities 更新實體

6 OnAuditDeleteOfEntity 刪除實體

OnAuditDirectDeleteOfEntities 刪除實體

7 OnAuditEntityFieldGet獲取屬性值

8 OnAuditLoadOfEntity加載實體

 

 

建立數據庫表AuditSetting 表示存儲數據庫表是否啓用審計功能(Audit)。

CREATE TABLE [dbo].[AuditSetting](
 [TableName] [NVARCHAR](100) NOT NULL,
 CONSTRAINT [PK_AuditSetting] PRIMARY KEY CLUSTERED 
(
    [EntityName] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

設計三個數據庫表,用於存放數據先後的值,用於數據審計。

--Audit  記錄用戶在每一個時間點操做了什麼功能
CREATE TABLE [dbo].[Audit]
(
[LogNo] [bigint] NOT NULL IDENTITY(1, 1),
[Date] [datetime] NULL,
[UserId] [nvarchar] (10) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[FunctionCode] [nvarchar] (8) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[Remarks] [nvarchar] (100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[Audit] ADD CONSTRAINT [PK_Audit] PRIMARY KEY CLUSTERED  ([LogNo]) WITH (FILLFACTOR=70) ON [PRIMARY]
GO

--Audit Table 記錄用戶操做的功能涉及到的表
CREATE TABLE [dbo].[AuditTable]
(
[LogNo] [bigint] NOT NULL,
[TableNo] [int] NOT NULL,
[Action] [int] NULL,
[EntityName] [nvarchar] (60) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[KeyValue] [nvarchar] (200) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[AuditTable] ADD CONSTRAINT [PK_AuditTable] PRIMARY KEY CLUSTERED  ([LogNo], [TableNo]) WITH (FILLFACTOR=70) ON [PRIMARY]
GO

ALTER TABLE [dbo].[AuditTable] WITH NOCHECK ADD CONSTRAINT [FK_AuditTrailTableDetail_AuditTrail] FOREIGN KEY ([LogNo]) REFERENCES [dbo].[Audit] ([LogNo]) ON DELETE CASCADE ON UPDATE CASCADE
GO


--Audit Table column detail 記錄列值的新值和舊值
CREATE TABLE [dbo].[AuditTableColumn]
(
[LogNo] [bigint] NOT NULL,
[TableNo] [int] NOT NULL,
[ColumnNo] [int] NOT NULL,
[ColumnName] [nvarchar] (100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[OldValue] [ntext] COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[NewValue] [ntext] COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[AuditTableColumn] ADD CONSTRAINT [PK_AuditTrailColumnDetail] PRIMARY KEY CLUSTERED  ([LogNo], [TableNo], [ColumnNo]) WITH (FILLFACTOR=70) ON [PRIMARY]
GO
ALTER TABLE [dbo].[AuditTableColumn] WITH NOCHECK ADD CONSTRAINT [FK_AuditTrailColumnDetail_AuditTrailTableDetail] FOREIGN KEY ([LogNo], [TableNo]) REFERENCES [dbo].[AuditTable] ([LogNo], [TableNo]) ON DELETE CASCADE ON UPDATE CASCADE
GO
 

定義一個數據審計類型(Facade外觀模式),用於數據審計操做。

[DependencyInjectionInfo(typeof(IEntity2), "AuditorToUse")]
[Serializable]
public sealed class DatabaseAuditor : AuditorBase, IDisposable
{
        #region Class Member Declarations
        private AuditEntity _auditTrail;
        private AuditTableCollection _auditTrailTableDetails;

由於我用的是LLBL Gen Pro的Adapter模式,因此爲數據訪問接口增長審計對象。

public sealed class DataAccessAdapter 
{
     private DatabaseAuditor _auditor;

     private void InitilaizeAuditor(IEntity2 entity)
     {
          _auditor = new DatabaseAuditor();
          _auditor.Adapter = this;
     }

     protected override void Dispose(bool isDisposing)
     {
            if (_auditor != null)
            {
                _auditor.Dispose();
                _auditor = null;
            }
     }

     public override void Commit()
     {
           if (_auditor != null)
           {
                _auditor.PersistAuditInfo();
                _auditor.Dispose();
                _auditor = null;
           }
     }
}
 
 

重寫了Commit方法,這代表與數據庫相關的操做事務提交時都會調用此方法,用於保存審計信息,也就是值的變化前和變化後的數據。

在保存字段數據時,注意上面的表AuditTableColumn的舊值OldValue和新值NewValue字段都是字符串類型,因此還須要寫一個方法,將.NET數據類型轉化爲字符串格式的值。

如何獲取實體屬性的舊值與新值,注意上面的代碼中用Commit做攔截,保存值數據,數據庫事物提交時,值尚未發生更改到數據庫中。對比實體的屬性新值和數據庫中的字段舊值,便可達到這個目的。

foreach (IEntityField2 field in  entity).Fields)
{
       string originalValue = GetFieldOriginalValue(field, false);
       string currentValue = GetFieldCurrentValue(field, false);
}

取舊值的方法,也就是取ORM屬性字段的DbValue,LLBL Gen Pro這一特性在通用功能設計中很是有用。

string fieldOldValue=string.Empty;
if (field.DbValue != null && field.DbValue != DBNull.Value)
{
     fieldOldValue= ConvertValueToString(field.DbValue, convertZeroToEmptyString);
}
 

最後上一張數據審計查詢結果的界面,幫助理解審計功能的設計。

image

數據審計給企業的審計部門提供了方便,也會下降系統性能,頻繁的記錄字段的舊值和新值,增長了事務的處理時間。

對一些不重要的業務數據,應該關閉審計選項,提供系統性能。

相關文章
相關標籤/搜索