I was investigating techniques to do auditing and think this is going to fit my needs, thought I would share. . . .
Let me start by saying there are about 1000 examples similar to this on the web. However none of the examples I found meet my specific requirements.
First lets discuss the requirements:
- I need to be able to apply auditing to only some of the types in my system
- I need to be able to ignore some of the properties on a type (i.e. not audit them)
- I need something flexible enough that I can apply it to multiple DbContexts
- I needed to have audit columns on each record (CreatedBy, CreatedOn, UpdatedOn, UpdatedBy).
- I needed a log table that would record when we got new records, when a field got changed, and when a record was deleted (including who and when it was done)
IAuditable Interface. I am using this guy to allow each type to “opt-in” to the auditing mechanism.
AuditIgnoreAttribute. I am using this to “opt-out” on a field by field basis of the auditing mechanism.
So now when I create my business object, I can mark him up appropriately depending on the requirements for that type. As you can see I want to audit my customer object, but not the Bio property.
Great now for the magic, the AuditableContext. This guy inherits from a normal DbContext, and overrides the SaveChanges method to add the auditing implementation. The auditing implementation is done in 5 steps:
- Figure out what the user changed and hang on to those changes for later
- Update the CreatedBy, CreatedOn, UpdatedOn, UpdatedBy columns as appropriate
- Perform a SaveChanges to write the users changes to the DB
- Update our list of user changes with the proper id’s (we need to do this because the data base is assigning the primary keys for our new entities)
- Save the list of user changes to the log table
That code should be straight forward. I did implement a helper method that figures out what fields we are interested by using a bit of reflection. NOTE: we are doing the reflection once per type and caching the results in a dictionary.
We only need to implement the AuditableContext once, each time we create a context in our app, we just inherit from it and we are off and to the races!
You can download a full working example from here