Logging Changes To Business Objects

Logging Changes To Business Objects

Old forum URL: forums.lhotka.net/forums/t/7668.aspx


MadGerbil posted on Tuesday, September 22, 2009

Ever since I first started using CSLA I've been plagued by the need to add code that logs changes to business objects. For each change that is made I record the old value, the new value, the property that was changed and who changed it. Such is the nature of accounting software.

Typically, the design involves a logs collection on the root object and all changes made to the root object or any child objects results in the creation of a new log in this collection.

For the longest time I've been adding code to do this in Set method for each property. While this could be done in CSLA 2008 (using my own backing fields) it is getting to the point where much of the benefit of the new framework would be lost.

I'm poking around for ideas on a good way to implement logging. It looks to me as if the ideal spot would be to add an additional method (or event) to the SetProperty() method of BusinessBase - right after the call to LoadPropertyValue as made on page 243 of the C# 2008 version of the text.

I thought maybe I could modify BusinessBase so that all objects marked as child would have a reference to the root objects logs collection. A new method in BusinessBase could write a log to that collection.

I'd like to leave the properties exactly like they are in the text so that I don't have to keep writting logging code. Any design advice is welcome.

rsbaker0 replied on Tuesday, September 22, 2009

Here's my approach:

See this thread for details (http://forums.lhotka.net/forums/post/35814.aspx )

Basically, you:

(1) Derive all your BO's that will implement auditing from an intermediate base class

(2) The intermediate Base class overrides OnPropertyChanging() (which works with CSLA managed fields as well as private backing fields if you call OnPropertyChanging() in your private backing field setter), and then you can capture changes to any property and log them as desired. The actual property value hasn't changed yet when this occurs, so both the original and new value are avaiilable.

JonnyBee replied on Thursday, September 24, 2009

Hi,

You could also create your own baseclasses and implement your own audit logging. Take a look at Jason Bocks article and code here: http://jasonbock.net/JB/Default.aspx?blog=entry.9cc70d85bef34e2b9a683ba82615f8a3  One of the samples he implements here is managed properties that keep the original field value and altered baseclasses for initializing/resetting the original values - in MarkOld/MarkClean). 

This would make the FieldManager provide an audit logger with Old/New values.

/jonnybee

MadGerbil replied on Thursday, September 24, 2009

Thanks gentlemen.

What is frustrating is that the current CSLA gets so very close to meeting my needs. I can capture the OnChildChanged event in the root object and almost get all the data I need to create a log.

For example, if PropertyChangedArgs had the new value and the old value built in (along with a couple of other properties) this whole thing would be very easy.

I love the idea of OnChildChanged bubbling all the way up to the root object - that is so convenient - and already there.

Could a solution be a custom implementation of the PropertyChangedArgs object?

I'll take a look at the two proposed solutions and toy around with some ideas of my own.

JonnyBee replied on Thursday, September 24, 2009

Hi,

I am not to keen about letting it all bubble up to the root object -  as it breaks encapsulation of functionality.

The Observer pattern could probably work but please do try different solutions and keep us posted.

/jonnybee

MadGerbil replied on Wednesday, October 07, 2009

I bubble it up to the root object because the root object maintains a collection of all changes made to itself (and all child objects). The audit logs are a collection off the root.

I'm an intermediate level programmer and I usually don't mess around under the hood. It has been interesting to "fiddle" with CSLA though, it has been very educational.

My challenge is to get info beyond what is communicated by the OnPropertyChanged event - I need more than the property name for an audit log. I need the property name, object type, property id, old value, new value and a few other things.

All of my changes involve messing around with CSLA code - if you aren't comfortable with that then any solution I have isn't for you.

I've no idea what I'm doing and have no capacity to anticipate the fact I may be creating a blackhole that will suck my employer into oblivion, but that has never stopped me before.

Stuff I've Messed With:
1: I derived a class from PropertyChangedEventArgs and added the properties I required. Thanks to contravariance, I could pass that new object all over the place just like it was PropertyChangedEventArgs and cast it to my derived class when I needed to create an audit log. I didn't like this approach, it scared me.

2: I've created an entirely different event in the BusinessBase and ExtendedBindingList so that just like PropertyChangedEventArgs and ListChangedEventArgs are passed into a ChildChanged event, so now event AuditEventArgs are passed into the ChildChangedEventArgs object.

With this approach, I raise an OnAudit event any time a property is changed on an object that is not new (no need to log changes on objects that are new).

The OnAudit method is called in LoadPropertyValue of the Core.BusinessBase right after the PropertyHasChanged method is called. Both are eventually wrapped up in the OnChildChanged event.

This doesn't handle the addition of new objects yet - that is still on my 'todo' list. I would probably also be better to separate this out from the OnChildChanged event entirely but I don't know if that makes any difference or not.

I could post the entire project in a zip file somewhere if people want to see it.

MadGerbil replied on Wednesday, October 07, 2009

BTW, we might as well be honest as to the real reason we have audit logs. Audit logs exist so that when users say, "I didn't do THAT" there is the ability to point to the log and say, "Uh, yes you did".

I cannot tell you how many times an audit log has saved me and quieted down a rather aggressive co-worker.

JonnyBee replied on Wednesday, October 07, 2009

Hi,

The reason I don't like to let it bubble up to the "root" object is:
1: When using BeginEdit/ApplyEdit/CancelEdit  you may roll back changes to child objects - these will not necessarily be removed from your "root" object (ie: you may be logging changes that never even got to the database).

2: I would rather have either
    * a seperate class that aggregates all the changes made to an object structure and save these to an audit trail
    * or have each BO (BusinessBase) save a list of modified fields to the audittrail.

Or if you can stick with SQLServer 2008 this type of AuditTrail is build in to the database iteself.

Copyright (c) Marimer LLC