解析大型.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

数据审计给企业的审计部门提供了方便,也会下降系统性能,频繁的记录字段的旧值和新值,增长了事务的处理时间。

对一些不重要的业务数据,应该关闭审计选项,提供系统性能。

相关文章
相关标签/搜索