數據審計,英語表達是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); }
最後上一張數據審計查詢結果的界面,幫助理解審計功能的設計。
數據審計給企業的審計部門提供了方便,也會下降系統性能,頻繁的記錄字段的舊值和新值,增長了事務的處理時間。
對一些不重要的業務數據,應該關閉審計選項,提供系統性能。