This is a little late – but I feel it is worth going into and you can catch up be reading (or re-reading) Part 1 located here. Just in case you are wondering “what on Earth is he babbling on about?” which is a fair and often under asked question, here’s a brief summary to refresh your memory.
The objective is to implement additional (background) functionality which is not seen by calling/consuming code – hence, transparent. In fact, it’s even possible to enforce execution of certain functionality no matter how a DataContext is manipulated – neat, huh?
So let us look at a simple database table and let us further assume you have generated a LINQ to SQL Model:
Now, let’s assume you will at some point create and update a Movie entity.
In code, by way of a simple example, you might have something like this (this is a primitive example):
Now – assuming you have a requirement to Audit all changes made to Movie data, how will you go about ensuring you capture all the changes made via LINQ to SQL – in this case, capturing the change from “New Movie” to “Newer Movie”?
Depending on your architecture and design, you’ll have various options. If you want maximum flexibility, then functional transparency might serve you well. Here is how you might accomplish this task, using the DataContext.
Now, first set a common base class for the Entities.
By default they will be generated without a common base. To do so using Visual Studio, you may edit the class definitions directly, or use SQLMetal to generate the entities. If you have a large data model, it might be recommended to use SQLMetal from the command line. For our purposes, the base could just be a public class with little to no implementation. Here I have added a base class to the Movie class definition:
Once you have set a base class, it’s time to establish our auditing functionality. Extending the Data Context is the next step. This is easier than you might think.
Add a new class to your solution. Ensure it is in the same namespace as your generated entities and data context. Give it the same name as your data context and ensure it is declared as a partial class.
For our example, we want to capture changes to the Movie table, so we implement the “UpdateMovie” function, by adding our own implementation. Don’t worry about the actual auditing part just yet. The important thing to learn here is that “ExecuteDynamicUpdate(instance)” must be called, or no actual update will occur to the entity.
Now, let us look at the Auditing functionality. I’ve implemented this functionality in a public static class called “Audit”. The function AuditChanges, accepts an entity derived from the base EntityBase and also takes the associated DataContext.
All that remains is to check if there are any pending changes and, if so, we can obtain them using the DataContext’s GetModifiedMembers function.
Now you have access to the original values and the modified values for each changed property. You can log the changes any way you like. Problem solved. This implementation allows you to use the same routine to audit from any of the entities, assuming you use the same base class, and implement the extended Data Context.
So to audit changes to the Actor table, you’d simply add this to the extended DataContext:
Hopefully this is a good enough example to introduce to you the merits of extending your DataContext. This is bu
t one minor example.
Other scenarios could include encryption/decryption of data stored in the database, inserting related records, setting concurrency (or checking timestamp values) and the list goes on.
None of this code here (in this blog entry) is production ready however It has proven on a large enterprise project to be a very reliable approach. You can factor this approach into your own logging, auditing and exception management design.
Feel free to contact me or leave comments if you have any questions.
P.S. Here is a cleaner shot of the Audit function: