【问题标题】:compare entities before update in Nhibernate (audit implementation )在 Nhibernate 更新之前比较实体(审计实现)
【发布时间】:2009-09-08 20:37:26
【问题描述】:

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

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

我需要将当前状态与实体的先前状态进行比较,但我不知道该怎么做(它应该是通用的解决方案,我可以应用于多种类型)

【问题讨论】:

    标签: c# .net nhibernate fluent-nhibernate


    【解决方案1】:

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

    我最终将 IInterceptor 实现为一个类和一个通用审计日志 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
    

    【讨论】:

    • 是否将 AuditLog 保存在与主实体相同的事务中?
    • 对我来说它应该是同一事务的一部分,这就是为什么我使用 UnitOfWork.Start(UnitOfWorkNestingOptions.CreateNewOrNestUnitOfWork) 以便它加入当前的 UOW。我知道当我试图让它发挥作用时,我在这方面遇到了问题。使用 CreateNewOrNestUnitOfWork 选项作为 UOW 的一部分会有所不同。
    【解决方案2】:

    我不确定这正是您要问的,b/c 似乎答案只是遍历 currentState 数组并与 previousState 数组进行比较以获得相同的索引值。被改变的属性名是同一索引处的propertyName值

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

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

    干杯!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-02
      • 1970-01-01
      • 2011-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-12
      • 1970-01-01
      相关资源
      最近更新 更多