Click here to Skip to main content
15,847,263 members
Articles / Programming Languages / C#

C# Object Cloning - For Business Applications

Rate me:
Please Sign up or sign in to vote.
3.38/5 (5 votes)
28 Mar 2009CPOL4 min read 22.4K   16   6
Not exactly a new technique for cloning, just a way of coding to meet business application needs.

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:
    1. Multiple fetches from database
    2. Using Reflection
    3. 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.

C#
namespace TestApp.Security.BLL.User
{
    public class UserController : BusinessController
    {
        ...
        ...
        // Public access methods on the controller
        public UserData New()
        {
            return this.NewUser();
        }
        public UserData Get(int userId)
        {
            return this.GetUser();
        }
        ...
        ...
        ...
        // Private methods, mainly work as helpers to public methods
        // 'da' is the Data Access Layer object implemented
        // with Enterprise Library Data Access
        private UserData NewUser()
        {
            // Creates a new user (returns a blank UserData object for data entry)
            // Note that the da.NewUser executes
            // a "SELECT * FROM Users WHERE 1 = 2" to get the 
            // schema and then appends a blank row
            // lke DataSet.Tables[0].Rows.Add(). So this NewUser
            // method in business logic layer gets blank row to load in the object.

            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;
        }
        // Gets an existing user from the database
        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)
        {
            // Get an empty business object (DTO) first
            UserData user = new UserData();

            // Get the row
            DataRow drUser = dsUser.Tables[0].Rows[0];
            // Load the DTO (loading directly to internal field variables)
            user._id = (int)ConvertDBNullToString(drUser["Id"], 0);
            user._name = (String)ConvertDBNullToString(drUser["Name"], "");
            user._fullName = (String)ConvertDBNullToString(drUser["FullName"], "");
            ...
            ...
            return user;
        }
    }
 
    // The UserData class is implemented as 
    public class UserData
    {
        // Fields and variables
        internal int _id;
        internal string _name, _fullname;
        internal UserData _old;

        // Public properties
        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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
India India
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralIf this technique fits ok Pin
Donsw27-Apr-09 8:44
Donsw27-Apr-09 8:44 
GeneralMy vote of 2 Pin
Josh Fischer28-Mar-09 6:33
Josh Fischer28-Mar-09 6:33 
GeneralRe: My vote of 2 Pin
Izzet Kerem Kusmezer28-Mar-09 16:12
Izzet Kerem Kusmezer28-Mar-09 16:12 
GeneralRe: My vote of 2 Pin
Dmitri Nеstеruk29-Mar-09 6:53
Dmitri Nеstеruk29-Mar-09 6:53 
GeneralRe: My vote of 2 Pin
Rajarshi (C# Pro)29-Mar-09 9:38
Rajarshi (C# Pro)29-Mar-09 9:38 
GeneralRe: My vote of 2 Pin
ramuknavap1-Apr-09 16:25
ramuknavap1-Apr-09 16:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.