捕获对象属性的变化

发布于 2024-09-25 13:52:29 字数 2657 浏览 8 评论 0 原文

我的应用程序中有多个业务对象(C#、Winforms、WinXP)。当用户在 UI 上执行某些操作时,应用程序的不同部分会修改和更新每个对象。每次修改后,我需要首先检查发生了什么变化,然后记录对对象所做的这些更改。记录此信息的目的是创建对应用程序中正在进行的活动的全面跟踪。

这些对象中的许多对象都包含其他对象的列表,并且这种嵌套可以是几层深。任何解决方案的两个主要要求是

  1. 尽可能准确地捕捉变化
  2. 将性能成本降至最低。

例如,对于业务对象:

public class MainClass1
{
    public MainClass1()
    {
        detailCollection1 = new ClassDetailCollection1();
        detailCollection2 = new ClassDetailCollection2();
    }

    private Int64 id;
    public Int64 ID
    {
        get { return id; }
        set { id = value; }
    }

    private DateTime timeStamp;
    public DateTime TimeStamp
    {
        get { return timeStamp; }
        set { timeStamp = value; }
    }

    private string category = string.Empty;
    public string Category
    {
        get { return category; }
        set { category = value; }
    }

    private string action = string.Empty;
    public string Action
    {
        get { return action; }
        set { action = value; }
    }

    private ClassDetailCollection1 detailCollection1;
    public ClassDetailCollection1 DetailCollection1
    {
        get { return detailCollection1; }
    }

    private ClassDetailCollection2 detailCollection2;
    public ClassDetailCollection2 DetailCollection2
    {
        get { return detailCollection2; }
    }

    //more collections here
}

public class ClassDetailCollection1
{
    private List<DetailType1> detailType1Collection;
    public List<DetailType1> DetailType1Collection
    {
        get { return detailType1Collection; }
    }

    private List<DetailType2> detailType2Collection;
    public List<DetailType2> DetailType2Collection
    {
        get { return detailType2Collection; }
    }
}

public class ClassDetailCollection2
{
    private List<DetailType3> detailType3Collection;
    public List<DetailType3> DetailType3Collection
    {
        get { return detailType3Collection; }
    }

    private List<DetailType4> detailType4Collection;
    public List<DetailType4> DetailType4Collection
    {
        get { return detailType4Collection; }
    }
}

//more other Types like MainClass1 above...

我可以假设我将有权访问该对象的旧值和新值。

在这种情况下,我可以想出两种方法来尝试执行此操作,而无需告知已明确更改的内容。

  1. 使用反射并迭代对象的所有属性并进行比较 那些具有相应的 旧对象的属性。日志 任何已更改的属性。这 方法似乎更加灵活, 我不必担心如果有的话 新属性被添加到任何 对象。但看起来也有性能 很重。
  2. 记录所有对象所有属性的设置器中的更改。 除了这将 需要我改很多代码,它 看起来更暴力。这将是 维护工作繁重且不灵活,如果 有人更新任何对象 类型。但这样也可能是 性能轻,因为我不会 需要检查更改的内容并记录 究竟更改了哪些属性。

欢迎提出任何更好的方法和/或改进上述方法的建议

I have multiple business objects in my application (C#, Winforms, WinXP). When the user executes some action on the UI, each of these objects are modified and updated by different parts of the application. After each modification, I need to first check what has changed and then log these changes made to the object. The purpose of logging this is to create a comprehensive tracking of activity going on in the application.

Many among these objects contain contain lists of other objects and this nesting can be several levels deep. The 2 main requirements for any solution would be

  1. capture changes as accurately as possible
  2. keep performance cost to minimum.

eg of a business object:

public class MainClass1
{
    public MainClass1()
    {
        detailCollection1 = new ClassDetailCollection1();
        detailCollection2 = new ClassDetailCollection2();
    }

    private Int64 id;
    public Int64 ID
    {
        get { return id; }
        set { id = value; }
    }

    private DateTime timeStamp;
    public DateTime TimeStamp
    {
        get { return timeStamp; }
        set { timeStamp = value; }
    }

    private string category = string.Empty;
    public string Category
    {
        get { return category; }
        set { category = value; }
    }

    private string action = string.Empty;
    public string Action
    {
        get { return action; }
        set { action = value; }
    }

    private ClassDetailCollection1 detailCollection1;
    public ClassDetailCollection1 DetailCollection1
    {
        get { return detailCollection1; }
    }

    private ClassDetailCollection2 detailCollection2;
    public ClassDetailCollection2 DetailCollection2
    {
        get { return detailCollection2; }
    }

    //more collections here
}

public class ClassDetailCollection1
{
    private List<DetailType1> detailType1Collection;
    public List<DetailType1> DetailType1Collection
    {
        get { return detailType1Collection; }
    }

    private List<DetailType2> detailType2Collection;
    public List<DetailType2> DetailType2Collection
    {
        get { return detailType2Collection; }
    }
}

public class ClassDetailCollection2
{
    private List<DetailType3> detailType3Collection;
    public List<DetailType3> DetailType3Collection
    {
        get { return detailType3Collection; }
    }

    private List<DetailType4> detailType4Collection;
    public List<DetailType4> DetailType4Collection
    {
        get { return detailType4Collection; }
    }
}

//more other Types like MainClass1 above...

I can assume that I will have access to the old values and new values of the object.

In that case I can think of 2 ways to try to do this without being told what has explicitly changed.

  1. use reflection and iterate thru all properties of the object and compare
    those with the corresponding
    properties of the older object. Log
    any properties that have changed. This
    approach seems to be more flexible, in
    that I would not have to worry if any
    new properties are added to any of the
    objects. But it also seems performance
    heavy.
  2. Log changes in the setter of all the properties for all the objects.
    Other than the fact that this will
    need me to change a lot of code, it
    seems more brute force. This will be
    maintenance heavy and inflexible if
    some one updates any of the Object
    Types. But this way it may also be
    preformance light since I will not
    need to check what changed and log
    exactly what properties are changed.

Suggestions for any better approaches and/or improvements to above approaches are welcome

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

時窥 2024-10-02 13:52:29

几年前我开发了一个这样的系统。这个想法是跟踪对象的更改并将这些更改存储在数据库中,就像对象的版本控制一样。

最好的方法称为面向方面编程,或AOP。您将“建议”注入到 setter 和 getter 中(实际上所有方法执行,getter 和 setter 都只是特殊方法),允许您“拦截”对对象执行的操作。查看 Spring.NETPostSharp 用于 .NET AOP 解决方案。

I developed a system like this a few years ago. The idea was to track changes to an object and store those changes in a database, like version control for objects.

The best approach is called Aspect-Oriented Programming, or AOP. You inject "advice" into the setters and getters (actually all method execution, getters and setters are just special methods) allowing you to "intercept" actions taken on the objects. Look into Spring.NET or PostSharp for .NET AOP solutions.

森林散布 2024-10-02 13:52:29

我可能无法给你一个好的答案,但我会告诉你,在绝大多数情况下,选项 1 并不是一个好的答案。在我们的项目中,我们正在处理一个非常相似的反思性“graph-walker”;当时看起来是一个好主意,但它是一场噩梦,原因如下:

  • 您知道对象发生了变化,但在反射性“更改处理”类中没有关于其上方对象的工作方式的高水平知识,您可能不知道为什么。如果该信息对您很重要,则必须将其提供给更改处理程序,最有可能通过域对象上的字段或属性,要求更改您的域并向域传授有关业务逻辑的知识。
  • 更改可能会影响多个对象,但可能不需要记录每个级别的更改;例如,当新贷款获得批准时,客户可能不希望在日志中看到借款人未偿还贷款计数的变化,但他们确实希望看到由于合并而发生的变化。在这些情况下,管理有关日志记录的规则需要更改处理类了解更多结构,而不仅仅是一个对象,这会很快使更改处理对象变得非常大且非常脆弱。
  • 您的图遍历器的要求可能比您知道的要多;如果您的对象图包含反向引用或交叉引用,则 Walker 必须知道它在哪里,最简单的综合方法是保留其处理的对象列表,并在处理之前对照其处理的对象检查当前对象(使反回溯成为 N^2 操作)。它还不得考虑对图中对象的更改,这些更改在您保留顶层时不会保留(非“级联”的引用)。 NHibernate 使您能够插入自己的图形遍历器并遵守映射中的级联规则,这会有所帮助,但如果您使用的是自己的 DAL,或者您确实希望记录对以下对象的更改: NHibernate 不会级联,你必须自己设置这一切。
  • 处理程序中的一段逻辑可能会进行更改,需要更新“父”对象(也许更新计算字段)。现在,如果更改对更改处理逻辑的另一部分感兴趣,则必须返回并重新评估更改的对象。
  • 如果您的逻辑需要创建和持久化新对象,则必须执行以下两件事之一:将新对象附加到图表中的某个位置(步行器可能会或可能不会拾取该对象),或者将新对象保留在其自己的事务中(如果您使用的是 ORM,则该对象不能引用另一个对象中的对象)具有“级联”设置的图表将导致首先保存它)。
  • 最后,在遍历图形和查找特定对象的“处理程序”时进行高度反思,将复杂的树传递到这样的框架中可以保证应用程序中的速度提升。

我认为,如果您跳过“更改处理程序”反射模式,并在您在业务中执行的“工作单元”中包含审核日志的创建或任何预持久性逻辑,您将为自己省去很多麻烦层,通过一组“审计记录器”。这允许进行更改的逻辑采用算法选择模式(例如命令或策略)来告诉您的审计框架到底发生了哪种更改,以便它可以选择将生成所需日志记录消息的记录器。

I may not be able to give you a good answer, but I will tell you that in the overwhelming majority of cases, option 1 is NOT a good answer. We're dealing with a very similar reflective "graph-walker" in our project; seemed like a good idea at the time, but it is a nightmare, for the following reasons:

  • You know the object changed, but without a high level of knowledge in the reflective "change handling" class about the workings of objects above it, you may not know why. If that information is important to you, you have to give it to the change handler, most l;ikely through a field or property on the domain object, requiring changes to your domain and imparting knowledge to the domain about the business logic.
  • Changes can affect multiple objects, but logs for changes at every level may not be desired; for instance, the client may not want to see a change to a Borrower's outstanding loan count in the log when a new Loan is approved, but they do want to see changes due to consolidations. Managing rules about logging in these cases requires change handling classes to know about more of the structure than just one object, which can very quickly make a change-handling object VERY big, and VERY brittle.
  • The requirements of your graph walker are probably more than you know; if your object graph includes backreferences or cross-references, the walker must know where it's been, and the simplest comprehensive way to do that is to keep a list of objects it's processed, and check the current object against those it's handled before processing it (making anti-backtracking an N^2 operation). It must also not consider changes to objects in the graph that will not be persisted when you persist the top level (references that are not "cascaded"). NHibernate gives you the ability to plug into its own graph-walker and abide by the cascade rukles in your mappings, which helps, but if you're using a roll-your-own DAL, or you DO want to log changes to objects that NHibernate won't cascade to, you're going to have to set this all up yourself.
  • A piece of logic in a handler may make a change that requires an update to a "parent" object (updating a calculated field, perhaps). Now, you have to go back and re-evaluate the changed object if the change is of interest to another piece of the change handling logic.
  • If you have logic that requires creation and persistence of a new object, you must do one of two things; attach the new object to the graph somewhere (where it may or may not be picked up by the walker), or persist the new object in its own transaction (if you're using an ORM, the object CANNOT reference an object from the other graph with a "cascade" setting that will cause it to be saved first).
  • Finally, being highly reflective in both walking the graph and finding the "handlers" for a particular object, passing a complex tree into such a framework is a guaranteed speed bump in your application.

I think you'll save yourself a lot of headaches if you skip the "change handler" reflective pattern, and include the creation of audit logs or any pre-persistence logic in the "unit of work" you're performing up at the business layer, through a set of "audit loggers". This allows the logic making the changes to employ an algorithm selection pattern such as Command or Strategy to tell your audit framework exactly what kind of change is happening, so it can pick the logger that will produce the required logging messages.

一梦浮鱼 2024-10-02 13:52:29

请参阅此处 adempiere 如何完成变更日志:http://wiki.adempiere.net/Change_Log

See here how adempiere did the changelog: http://wiki.adempiere.net/Change_Log

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文