4

我现在正在使用该IPostUpdateEventListener界面来更新审计日志,获取旧值和新值,然后将每个更新的字段存储在“审计”表中以及所有这些内容。效果很好,但是我很难满足最后两个要求:

  1. 显示更新是针对哪个员工的。
  2. 显示更新的字段的“友好”名称。

对于#1,我的第一直觉是使用反射并查找并获取给定实体上的“员工”属性以找出它是为哪个员工服务的,但是当您在图中深入一些对象时,它很快就会分崩离析没有自动返回给定 Employee 对象的方法。

解决 #1 的想法包括要求每个对象都有一个“父”属性,这样我就可以遍历图表以查找 Employee 类型(对我来说,对于简单的持久性问题来说,这会过多地污染我们的域)到使用单独的 SQL事后遍历外键并填写员工 ID 的作业(我宁愿不维护单独的 SQL 作业,因为到目前为止一切都是基于代码的 - 而且该 SQL 作业很快就会变得非常讨厌)。

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

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

对于这两个问题的想法将不胜感激。几天来搜索和绞尽脑汁并没有发现任何用处——大多数人似乎只停留在简单的旧/新淡水河谷记录,或者只是记录本身的“创建/更新”时间戳。

4

4 回答 4

3

我有一个和你类似的要求。就我而言,它是一个医疗保健应用程序,审核日志需要识别插入/更新适用的患者。

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

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; } }
}

然后获取属性IPostInsertEventListener以填充审计记录。IPostUpdateEventListenerOwningPatient

该解决方案的优点是将审计逻辑保留在事件侦听器中,这是唯一可以确定插入/更新将发生的地方,以及允许遍历对象与其拥有的患者之间的间接链接。

缺点是被审计的类必须从特定的接口派生。我认为好处超过了这个小成本。

于 2009-08-03T11:59:50.170 回答
0

这似乎是一个比琐碎的审计跟踪更复杂的场景,因此我倾向于使用应用程序服务来处理这个问题。

  1. 它更可测试。
  2. 它的明确。
  3. 您不必求助于反射或公共属性。

我们在我们的应用程序中使用审计服务,即使我们的场景比您的场景简单得多,因为它是一个域问题。我们的域了解历史并与历史一起工作,它不仅仅是为某些报告前端而创建的。

您是在记录谁(员工)在进行更新,还是员工是某种聚合根?

于 2009-07-27T20:30:10.113 回答
0

对于 #1,我设计了以下内容: 在 MyCommon 程序集中,我声明了

public interface IUserSessionStore
{
    UserSession Get();
    void Set(UserSession userSession);
}

它是使用 Ninject 为每个请求创建的。这个类的实现,只是简单的将 UserSession 对象存储在 ASPNET MVC Session 中。

有了这个,我可以在所有层中请求 IUserSessionStore 并获取 UserSession 供此类使用并作为审计信息插入到每个对象中(下面的类将在 MyModel 项目中):

public class AuditUpdater : DefaultSaveOrUpdateEventListener
{
    private IUserSessionStore userSessionStore;
    public AuditUpdater(IUserSessionStore userSessionStore)
    {
        this.userSessionStore = userSessionStore;
    }

    private Guid GetUserId()
    {
        return userSessionStore.Get().UserId;
    }

    private void UpdateAuditCreate(IAuditCreate auditable)
    {
        if (auditable != null)
        {
            auditable.CreationDate = DateTime.UtcNow;
            auditable.CreatedBy = GetUserId();
        }
    }

    .......
}

因此,您可以对其进行调整以获取您需要的员工信息。

很高兴我会接受更多建议!

于 2014-05-06T15:52:23.450 回答
0

要创建日志,您可以使用 IPostInsertEventListener 或 IPostUpdateEventListener。如果您使用的是流利的配置,您可以按照下面的示例进行配置。

当有发布或更新提交时将调用这些事件。

.ExposeConfiguration(c => c.EventListeners.PostCommitInsertEventListeners = new IPostInsertEventListener[] { new AuditEventPostInsert() })
.ExposeConfiguration(c => c.EventListeners.PostCommitUpdateEventListeners = new IPostUpdateEventListener[] { new AuditEventPostUpdate() });
于 2016-05-03T22:20:49.037 回答