So the other day a customer of mine asks whether it's possible to keep track of the 'Status' property on all 'WorkItem' entities. Basically he wanted to know who changed what value and when did that change occur. Auditing is not uncommon among business applications, and it's my safe bet that more properties will be required to keep track of in the future. So better do this the right way from the start then!
Weapon of choice
First of all let's evaluate the options for implementing auditing in a typical ASP.NET application using Entity Framework Code First:
- Custom business logic
- Database triggers
- Marking model properties and let the DAL-infrastructure do the magic
I don't like using custom business logic to track changes: it's too intrusive in the code, error-prone and costs a lot of maintenance. Using database triggers is then again too invisible. I like to keep my business requirements defined in one place, not scattered across tiers. The third option sounds good: it's clear what is being audited, but the details on the how are somewhere in the infrastructure. This keeps the business logic clean and focused on what it's actually trying to do, but you do get an indication some auditing is going on.
Marking the Auditables
Indicating an object's properties is being audited is somewhat the same as using the well-known marker interfaces, like IIdentifiable, ICreateable, IChangeable and IDeletable. Enter IAuditable:
Next we need the ability to specify what properties are being audited using our own Audited-attribute.
Bringing those two together in our POCO model gives us the following:
While it's not absolutely necessary to use the marker interface IAuditable, I do find it clear what's going on when I first look at a class definition. Also we're not going to waste time later on asserting all properties when there's no IAuditable implemented on class level.
Under the hood
So now that we have the ability to define auditing behavior in our model, let's look at the implementation. This is done in the DbContext's SaveChanges method. By overriding this method we can hook in our own logic just before or after EF is actually committing the changes made.
The UpdateIAuditables method requests all tracked objects which implement IAuditable and have been changed. After that all audited properties of these objects are being evaluated whether they've been changed or not. Finally the change event is being saved by AddAuditChange. In this case I'm using a simple generic entity called Audit and use the same EF context to store this change. You can easily implement your own audit storage of course, like for instance Azure's table storage.
Using a marker interface and attributes I can now very easily keep track of specific properties of the model. Based on the situation the implementation can be changed to store data a bit more sophisticated, while the model stays the same. Also the business logic is not cluttered with auditing logic, keeping focus on the more important requirements.