Introduction
Recently, I was looking for different techniques for object cloning in C# to achieve a simple objective in my business logic layer. I needed to create a snapshot of my object as it is loaded from the database for modification. The snapshot should store all properties of the original object (including value types and reference types), and will have a copy of them rather than any references. The snapshot will be later used by several business logic checks and validations where a comparison is required between the current or proposed value (by the user) and the old or persisted value (in the database). A quick analysis will reveal that it requires a Deep Copy of the object, and I started searching on several techniques of cloning and deep copy.
I found a good analytical comparison of different techniques of cloning and copying here.
Though I found the Binary Serialization technique very tempting and was about to implement it in my business logic layer, a second thought forced me to see things from a different perspective. On analysis, my objectives were revealed as:
- Create a deep copied snapshot of the business object as soon as it is loaded from the database.
- To use the snapshot to compare old values (database persisted) vs. new values (user proposed) for validation and other purposes in the business layer.
- Code should be easy to understand and implementable by beginners as well (we have a number of junior coders).
- Code should be easy to maintain - we should not lose sleep over the thought of not updating the copy/clone code every time a property is changed in the business object.
- Performance - any mechanism of cloning and/or copying would have a performance hit, so it is just a matter of choice how much is affordable. In my case, I ideally would try to avoid the following for performance hits:
- Multiple fetches from database
- Using Reflection
- Using Serialization
It seemed that the only way left to do things was to copy the properties manually, but that would mean maintaining the copying code diligently whenever any property in the business object is added/updated.
Considering the merits and demerits of all the above, I resorted to a style of coding that helped solve my goal of creating an object snapshot with minimum overheads. I re-iterate that it is just a style of coding and no rocket-science. Just wanted to share in case this suits your goals as well.
Using the code
In my architecture, I have business objects which are basically data containers, more like a DTO (Data Transfer Object), and there are business object controllers. While the DTOs are mere data containers with no business logic whatsoever in them, the business object controller classes are responsible for loading/saving/validating data in and out of the DTOs. All business logic resides in the respective business object controller only. Frankly, I feel that this architecture is really simple, yet sophisticated enough to serve typical small to medium scale business application goals, and may be scaled to enterprise level as well. I leave the architecture discussion as a topic for my next post.
Coming back to the point, the code in the business object controller contains public methods like New()
and Get(id)
. The New()
method creates a blank template of the business object for data entry, and the Get(id)
returns an object with the specified ID value from the database. These methods first get the relevant datasets supplied by the data access layers and then load the values from the datasets into the relevant object.
I then added two properties to my business object, namely old
and current
. The 'current
' property will contain a reference to the current object while the 'old
' property will have a reference to another copy of the same object. That 'another' copy is just what it means; it is a different copy, located at a different memory location, and is not touched even as the current copy is edited. So, whenever any old value reference is required for comparison, I can get it as myobject.old.myproperty; the current object properties can be referenced as myobject.current.myproperty or simply myobject.myproperty.
Following is an example considering a 'User' business object.
namespace TestApp.Security.BLL.User
{
public class UserController : BusinessController
{
...
...
public UserData New()
{
return this.NewUser();
}
public UserData Get(int userId)
{
return this.GetUser();
}
...
...
...
private UserData NewUser()
{
UserData current, old;
DataSet dsUser = da.NewUser();
DataSet dsUserRoles = da.GetUserRoles(0);
current = this.LoadData(dsUser, dsUserRoles);
old = this.LoadData(dsUser, dsUserRoles);
current._old = old;
return current;
}
private UserData GetUser(int userId)
{
UserData current, old;
DataSet dsUser = da.GetUser(userId);
DataSet dsUserRoles = da.GetUserRoles(userId);
current = this.LoadData(dsUser, dsUserRoles);
old = this.LoadData(dsUser, dsUserRoles);
current._old = old;
return current;
}
...
...
private UserData LoadData(DataSet dsUser, DataSet dsUserRoles)
{
UserData user = new UserData();
DataRow drUser = dsUser.Tables[0].Rows[0];
user._id = (int)ConvertDBNullToString(drUser["Id"], 0);
user._name = (String)ConvertDBNullToString(drUser["Name"], "");
user._fullName = (String)ConvertDBNullToString(drUser["FullName"], "");
...
...
return user;
}
}
public class UserData
{
internal int _id;
internal string _name, _fullname;
internal UserData _old;
public UserData Old
{
get { return _old; }
internal set
{
_old = value;
}
}
public UserData Current
{
get { return this; }
}
}
}
That's it. The bottom line is I am firing the LoadData
twice in the GetUser
or NewUser
method, once to load data in the current object and the second time to create a new object and keep it as 'old' in the current object itself. Though it might seem that the same code (LoadData
) is run twice always even if we do not need to have an old values snapshot, in my case, I almost always will be benefitted to have both the old and new values in the business object, for working within the business logic layer.
Regarding performance, running just a few lines of assignment code (from dataset to object) in process memory, I think, is far cheaper than checking with the database directly (to get the old values when required) or using Reflection or Binary Serialization.
That serves my purpose, and just storing a snapshot of old values of an object is a simple style of coding you can use. Obviously, there may be other more elegant methods and solutions, and I would love to know more about them. Reach me at rajabasuroy@hotmail.com.