复杂的NHibernate审核 [英] Complex NHibernate Auditing

查看:107
本文介绍了复杂的NHibernate审核的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我现在使用IPostUpdateEventListener界面进行更新审核日志记录,获取旧值和新值,然后将每个更新的字段存储在"Audit"表中,并进行所有操作.工作正常,但是我有两个最后的要求很难满足:

  1. 显示更新的对象.
  2. 显示已更新字段的友好"名称.

对于#1,我的第一个直觉是使用反射并寻找&在给定实体上获取"Employee"属性,以找出其适用于哪个Employee,但是当您在图形中很深处有一些对象时,这种方法很快就会崩溃,并且没有自动返回给定Employee对象的方法.

解决#1的想法的范围从在每个对象上都需要一个"Parent"属性,以便我可以遍历该图以查找Employee类型(对我来说,对于一个简单的持久性考虑,它将对我们的域造成过多的污染)到使用一个单独的SQL作业以遍历foriegn键并在事实之后填写Employee ID(我宁愿不维护一个单独的SQL作业,因为到目前为止所有内容都是基于代码的-并且该SQL作业将很快变得非常讨厌). /p>

对于第二个要求,我可以得到更改后的实际属性名称.对于我们80%到90%的字段,我们将显示(正确格式化的)属性名称,因此我可以根据Pascal大小写对名称进行间隔.但是,由于各种原因,其余字段不匹配.我们使用的是来自MvcContrib的ASP.NET MVC和Fluent HTML构建器,但是即使我们修改了设置,以至于视图模型上具有覆盖字段名称应具有的属性(因此将其保存在代码中而不是在代码中)只是视图),没有真正的方法可以将视图模型中的这些属性与要保存的域对象进行匹配.

对这两个问题的最终务实解决方案是在另一个服务中的每个更新操作之后调用审核日志记录服务,根据需要传递字段名称和员工信息,但是,我真的不想去有明显的原因.

任何一个问题的想法将不胜感激.搜索和绞尽脑汁几天没有发现任何用处-大多数人似乎都停止了简单的旧/新唱片录制,或者只是唱片本身的创建/更新"时间戳.

解决方案

我有一个与您相似的要求.就我而言,这是一个医疗保健应用程序,审核日志需要标识出插入/更新适用的患者.

我的解决方案是定义一个接口,所有审核的类都需要实现该接口:

public interface IAuditedRecord
{
    IPatient OwningPatient { get; }

    ...
    // Other audit-related properties (user, timestamp)
}

然后,经过审核的类将以所需的任何方式实现此接口.例如:

public class Medication : IAuditedRecord
{
    // One end of a bidirectional association. Populated by NHibernate.
    private IPatient _patient;

    IPatient OwningPatient { get { return _patient; } }
}

public class MedicationNote : IAuditedRecord
{
    // One end of a bidirectional association. Populated by NHibernate.
    private Medication _medication;

    IPatient OwningPatient { get { return _medication.OwningPatient; } }
}

IPostInsertEventListenerIPostUpdateEventListener然后获取OwningPatient属性,以填充审核记录.

该解决方案的优点是将审核逻​​辑保留在事件侦听器中,这是确保可以进行插入/更新的唯一位置,并且允许遍历对象与其对象之间的间接链接.拥有病人.

缺点是,已审计的类必须从特定的接口派生.我认为收益大于这笔小小的成本.

I'm using the IPostUpdateEventListener interface to do update audit logging now, grabbing the old and new values, then storing each updated field in an "Audit" table and all that jive. Works swell, but there's two last requirements I'm having a hard time fullfilling:

  1. Display which employee the update was for.
  2. Display the "friendly" name of the field updated.

For #1, my first instinct was to use reflection and look for & grab the "Employee" property on the given entity to find out which Employee it was for, but that quickly falls apart when you're a few objects deep in the graph with no automatic way to get back to the given Employee object.

Ideas to solve #1 ranged from requiring a "Parent" property on every object so I can traverse the graph looking for the Employee type (which, to me, would pollute our domain too much for a simple persistance concern) to using a separate SQL job to tranverse the foriegn keys and fill in the Employee ID after the fact (I'd rather not maintain a separate SQL job as everthing is code based thus far - and that SQL job will get quite nasty very quick).

As for the second requirement, I can get the actual property name that changed just fine. For a good 80% - 90% of our fields, the (properly formatted) property name is what we display, so I can just space the name based on the Pascal casing. The rest of the fields, however, don't match up for various reasons. We're using ASP.NET MVC and Fluent HTML builders from MvcContrib, but even if we modified the setup to the point of having an attribute on the view model that overridess what the field name should be (and therefor having it in code instead of just the view), there's no real way to match those attributes from the view models to the domain objects being saved.

A final pragmatic solution to both problems would just be to call an audit logging service after each update operation in another service, passing in the field names and employee information as needed, but, well, I really don't want to go there for obvious reasons.

Ideas for either problem would be greatly appreciated. Searching and racking my brain for a couple of days has turned up nothing of use - most people seem to stop at simple old/new vale recording or just a "created/updated" timestamp on the record itself.

解决方案

I have a requirement similar to yours. In my case, it's a health care application and the audit log needs to identify the patient to which the insert/update applies.

My solution is to define an interface, which all audited classes need to implement :

public interface IAuditedRecord
{
    IPatient OwningPatient { get; }

    ...
    // Other audit-related properties (user, timestamp)
}

The audited classes then implement this interface in whatever way is required. For example:

public class Medication : IAuditedRecord
{
    // One end of a bidirectional association. Populated by NHibernate.
    private IPatient _patient;

    IPatient OwningPatient { get { return _patient; } }
}

public class MedicationNote : IAuditedRecord
{
    // One end of a bidirectional association. Populated by NHibernate.
    private Medication _medication;

    IPatient OwningPatient { get { return _medication.OwningPatient; } }
}

The IPostInsertEventListener and IPostUpdateEventListener then fetch the OwningPatient property in order to populate the audit record.

The solution has the advantages of keeping the auditing logic in the event listeners, which is the only place where one is sure that an insert/update will take place, as well as allowing the traversal of indirect links between an object and its owning patient.

The downside is that the audited classes have to derive from a specific interface. I think the benefits outweigh this small cost.

这篇关于复杂的NHibernate审核的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆