Table of contents
Introduction and goal
Data audit trail is one of the most required features in any project. This article will talk about audit trail through application using the Prototype pattern. Most projects have a three tier architecture and have the core business objects in place. In this article, we will see how we can leverage existing business objects to implement auditing functionalities.
I am not saying that this is the best way. There are many other ways like dataset, triggers, etc. In this article, we will only look at business object audit trailing.
Currently I am circulating my free .NET eBook. I have collected around 500 FAQ questionnaire, hope you enjoy it… http://www.questpond.com/SampleDotNetInterviewQuestionBook.zip.
Screenshot of what we are targeting
Approaches of implementing an audit trail
An audit trail in a project can be implemented in two basic ways:
- First is through the application
- Second is using database functionalities like triggers or Stored Procedures
This article will concentrate on the first approach, i.e., through the application. We will use the Prototype pattern to create a clone of the old values and then do auditing.
The customer and supplier projects
For our auditing project, we will take up a sample project which has customer and supplier data. There are two tables and we need to maintain one as the customer table and the other as the supplier.
|Supplier Table |
|Sup_code ||This is a simple supplier code which is unique to each supplier. |
|Sup_telephone ||This contains the telephone. |
|Customer table |
|Name ||This attribute defines the name for the customer. |
|Address ||This property holds the address of the customer. |
Implementing the three tier architecture
We will first create a simple three tier architecture which will do our insert/update/delete. We will then build our audit trail feature using the three tier architecture. Below is the three tier view of the customer component. We have the UI ASPX pages which are consuming the customer component and the customer component in turn calls the database for insert, update, and delete operations. In the same manner, we will implement the supplier. For simplicity sake, we are not showing the supplier class here.
Visualizing the methodology for the audit trail
When talking about data auditing, we will need two things: old values and new values.
The class is a data container and the properties of the class hold the current, recent, and changed values. In other words, the ‘new value’ is contained in the private properties of the class. Now remains the issue of where we should maintain the old values.
So the approach for this is we will create the object of the class within itself. This object will hold the old value snapshot.
So how do we create a copy of the customer object?
GetCustomerData method is the place where we load the current object and this is also the place where we need to create the copy of the object. We will be using the Prototype pattern to create the clone of the customer object. In the next sections, we will just run through the basics of the Prototype pattern and then implement the audit trail functionality.
Understanding the Prototype pattern
The Prototype pattern falls in the section of Creational patterns. It gives us a way to create new objects from existing instances of an object. In one sentence, we clone the existing object with its data. By cloning, any changes to the cloned object does not affect the original object value. If you are thinking that by just setting objects we can get a clone, then you are mistaken. By setting an object to another object, we set the reference of the object ByRef. So changing the new object also changes the original object. To understand the ByRef fundamentals more clearly, consider the figure below. Following is the sequence of the code:
- In the first step, we create the first object, i.e.,
- In the second step, we create the second object, i.e.,
- In the third step, we set the values of the old object, i.e.,
obj1, to ‘old value’.
- In the fourth step, we set
- In the fifth step, we change the
- Now we display both the values and we find that both the objects have the new value.
The conclusion of the above example is that objects when set to other objects are set ByRef. So changing new object values changes the old object values as well.
There are many instances when we want that the new copy object changes not affect the old object. The answer to this is the Prototype pattern.
Let us look at how we can achieve this using C#. In the figure below, we have the customer class
ClsCustomer which needs to be cloned. This can be achieved in C# by using the
MemberWiseClone method. In Java, we have the
Clone method to achieve this. We have also shown the client code. We have created two objects of the customer class,
obj2. Any changes to
obj2 will not affect
obj1 as it’s a completely cloned copy.
Shallow and deep cloning
There are two types of cloning for Prototype patterns. One is shallow cloning which you have just seen in the first question. In shallow copy, only that object is cloned, any objects contained in that object is not cloned. For instance, consider the figure ‘Deep cloning in action’. We have a customer class and we have an address class aggregated inside the customer class.
MemberWiseClone will only clone the customer class
ClsCustomer but not the
ClsAddress class. So we add the
MemberWiseClone function in the address class also. Now when we call the
getClone function, we call the parent cloning function and also the child cloning function, which leads to cloning of the complete object. When the parent objects are cloned with their containing objects, it’s called deep cloning, and when only the parent is cloned, it is termed as shallow cloning.
Implementing the cloning feature in the customer class
As said before, the
getCustomerData function loads the values in the class. As soon as the class loads its own value, we will load a clone of the value in the cloned object inside the same class. The code snippet below shows how we have used
memberwiseclone to create a by value decoupled object inside the customer class.
The audit table design
I am sure as per project needs, the audit table design can be very complicated. But for our simplicity purpose, we will have the audit table as shown below.
|Audit table design |
|ID ||A simple identifier which defines a unique key |
|Old value ||This will hold the field name + the old values for the field name |
|New value ||This will hold the field name + the newly changed values |
The audit class, the plumbing block
When talking about object oriented programming, every class should do its job. So the customer class should do validation related to its properties while the supplier should do validations related to the supplier table. The audit functionality is neither the job of the supplier nor the job of the customer. It’s a technical functionality, in other words, it’s a plumbing block.
So we have created a
clsAudit class which will take in the current customer object as well as the cloned object and then adds it to the audit table we discussed in the previous section.
We can improve this functionality and make it more generic by using Reflection. For simplicity sake, we have currently used a simple
if condition to achieve our task.
Calling the audit class in the customer
So now we are finally at the end of the movie. We just consume the audit object in the customer class and do auditing.
public void Update(clsCustomer objCustomer, int CustomerId)
objcustomerClone = (clsCustomer)this.getClone();
objDataLayer.UpdateCustomerDB(Name, Address, CustomerId);
Below is a nice Design Pattern youtube video which explains step by step how to use Design pattern in C# projects.
The source code has audit trailing for both the customer and supplier tables.