在Nhibernate中更新之前比较实体(审计实现)

时间:2009-08-26 10:03:14

标签: c# .net nhibernate fluent-nhibernate

我需要在任何记录上的每个用户更新登录数据库,并且需要存储已更改的任何记录的旧值和新值。这个审计是作为nhibernate中的拦截器实现的,所以我有方法:

 public override bool OnFlushDirty(
            object entity,
            object id,
            object[] currentState,
            object[] previousState,
            string[] propertyNames,
            IType[] types)

我需要将当前状态与之前的实体状态进行比较,我不知道如何做到这一点(它应该是我可以应用于多种类型的通用解决方案)

2 个答案:

答案 0 :(得分:3)

我回答了一个类似的问题。 Audit Logging Strategies也以不同的方式看待这个问题 how-do-i-implement-changetime-and-changeuser-columns-using-nhibernate

我最终将IInterceptor实现为一个类和一个通用auditlog poco类,其中包含实体类型,实体ID,属性名称,当前状态,以前的状态,更改类型(插入,更新,删除)和发生变化的日期时间。

然后我创建了一个空接口IAuditable,以便识别我想要审计的每个类。

在OnFlushDirty,OnDelete和我现在无法想到的另一个事件中,为每个已更改的属性添加了新的审计日志类。

当我回到家并有权访问我目前使用的代码时,我会更新这个问题。

另外看 Frederik Gheysels' DevLog - NHibernate IInterceptor: an AuditInterceptor

修改 - 通过实施更新答案
再看一遍,我需要重新调整它,因为它确实有很多味道,但当时我只需要一些工作,这就是我想出来的。

这是我使用的nhibernate映射

    <class name="AuditLog" table="AuditLog" lazy="true" >
        <id name="_persistenceId" column="Id" type="Guid" access="field" unsaved-value="00000000-0000-0000-0000-000000000000" >
          <generator class="guid.comb" />
        </id>
        <version name="_persistenceVersion" column="RowVersion" access="field" type="int" unsaved-value="0"/>
        <property name="CreatedDate" column="CreatedDate" type="DateTime" />
        <property name="UpdatedBy" column="UpdatedBy" type="string" length="100" />
        <property name="EntityID" column="EntityID" type="guid" not-null="true" />
        <property name="EntityName" column="EntityName" type="String" length="100" not-null="true" />
        <property name="PropertyName" column="PropertyName" type="String" length="100" not-null="true"  />
        <property name="ActionType" column="ActionType" type="Char" length="1" not-null="true" />
        <property name="OldValue" column="OldValue" type="String" length="1000"   not-null="false" />
        <property name="NewValue" column="NewValue" type="String" length="1000"   not-null="false" />
    </class>

该类是一个通用的poco类,它实现了每个属性。

IInterceptor的实现看起来像这样(在VB.Net中)

Imports Rhino.Commons
Public Class AuditInterceptor
Inherits NHibernate.EmptyInterceptor
Private _auditLogRepository As IAuditLogRepository
Public Sub New(ByVal [AuditLogRepository] As IAuditLogRepository)
    _auditLogRepository = [AuditLogRepository]
End Sub
Public Overrides Function OnFlushDirty(ByVal entity As Object, ByVal id As Object, ByVal currentState() As Object, ByVal previousState() As Object, ByVal propertyNames() As String, ByVal types() As NHibernate.Type.IType) As Boolean
'Called on an Update

    If TypeOf entity Is IAuditable Then
        Using uow = UnitOfWork.Start(UnitOfWorkNestingOptions.CreateNewOrNestUnitOfWork)
            If TypeOf entity Is [yourObject] Then
                aLog = New AuditLog(Now, My.User.Name)
                With aLog
                    .EntityID = id
                    .EntityName = "[yourObject]"
                    .PropertyName = "[yourProperty]"
                    .ActionType = "U"
                    .OldValue = GetPropertyValue("[yourProperty]", previousState, propertyNames)
                    .NewValue = GetPropertyValue("[yourProperty]", currentState, propertyNames)
                End With
                _auditLogRepository.Save(aLog)    
            End if
            uow.Flush()
        End Using
    End If

    Return MyBase.OnFlushDirty(entity, id, state, propertyNames, types)
End Function
Public Overrides Function OnSave(ByVal entity As Object, ByVal id As Object, ByVal state() As Object, ByVal propertyNames() As String, ByVal types() As NHibernate.Type.IType) As Boolean
'Called on an Insert

    If TypeOf entity Is IAuditable Then
        'create a new audit log class here
    end if
    Return MyBase.OnSave(entity, id, state, propertyNames, types)
End Function

答案 1 :(得分:0)

我不确定这正是你所要求的,b / c似乎答案只是循环通过currentState数组,并与相同索引值的previousState数组进行比较。要更改的属性名称是同一索引处的propertyName值

我会提醒你,如果你在会话之外修改你的实体(Nhibernate称之为“分离”模型)我不认为以前的状态被填充(或者它会导致代价高昂的重读数据)它)。

我的建议是在数据库表上使用触发器来进行审计日志记录。恕我直言,以这种方式这样做更容易,甚至在你不通过NHib更新时也能正常工作

干杯!