Click here to Skip to main content
Licence CPOL
First Posted 7 May 2007
Views 24,738
Downloads 144
Bookmarked 35 times

Dynamic Data Mapping (From Database to Class Instance)

By | 7 May 2007 | Article
Dynamically assigns the value from the database to the properties of a class.
 
Part of The SQL Zone sponsored by
See Also

Introduction

This application helps in dynamically assigning values of the result retrieved from a database to the object (class type). In short, create a business object from each row of data dynamically. This will be helpful in creating business object collections in the business layer of a simple framework.

DynamicDataMapping class

  • Maps the column name of the resultant retrieved from the database to the properties of the class.
  • It sets the value to the object's properties from the resultant.

How to apply

  • Add the DynamicDataMapping class to your application.
  • Create a business object with specific properties (for example: class User).
  • Execute your Select query which is relevant to this object and get the resultant (normally, a resultant may be either a DataSet, DataTable, or DataReader (collection of rows)).
  • Call the Copy method of DynamicDataMapping and pass the resultant and the business object instance .
  • This will return you the business items with the state (attribute) of the object being filled with the values from the resultant rows.
  • Collect the item in a container.

Using the code

User class methods returning collection object

#region GetInstanceCollection (2 -Overloads)
//Gets the resultant as input and returns the Collection of object
private SortedList<int, User> GetInstanceCollection(DataTable dtResultTable)
{
    try
    {
        SortedList<int, User> itemList =null;
        if (dtResultTable != null)
        {
            itemList = new SortedList<int, User>(dtResultTable.Rows.Count);
            //loop thru each row inthe result table 
            foreach (DataRow dataRow in dtResultTable.Rows)
            {
                User objUser = new User();
                DynamicDataMapping dmap = new DynamicDataMapping();
                //copy value from the dataRow to the specified object
                dmap.Copy(dataRow, objUser, false, null);
                //collect the objects in a container with Primary key as Key
                itemList.Add(objUser.UserID, objUser);
            }
        }
        return (itemList);
    }
    catch { throw; }
}
 
//Calls the above methods to return the result Object Collection
public SortedList<int, User> GetUsers(DataTable ds)
{
    try
    {
        if (ds != null && ds.Rows.Count > 0)
        {
            return GetInstanceCollection(ds);
        }
        return null;
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

DynamicDataMapping class

Mapping

The Map method of the DynamicDataMapping class maps the source DataRow's columns with the target object's (class) properties. If the column name matches the property name, it sets the values to the properties. You may retrieve N number of columns from the database, but it doesn't mean you should have all the properties in your designed class. In case you do not have the specific column name as a property in your designed class, then add the column names in the IgnoreList. Here, the source DataRow's column name and the value of the column are retrieved and sent to the Set property method.

#region Map (DataRow - Object of type Class)

/// <summary>
/// Copies values from the source into the
/// properties of the target.
/// </summary>

/// <param name="source">A datarow containing the source values.</param>
/// <param name="target">An object with properties to be set from the datarow.</param>
/// <param name="ignoreList">A list of property names to ignore. 
/// These properties will not be set on the target object.</param>
/// <param name="suppressExceptions">If <see 
///      langword="true" />, any exceptions will be supressed.</param>
/// <remarks>
/// The column names in the datarow must match the property names on the target
/// object. Target properties may not be readonly or indexed.
/// </remarks>

private void Map(System.Data.DataRow objSource, Object objTarget, 
        bool blnSuppressExceptions, params string[] strIgnoreList)
{
    List<string> ignore =null;
    if(strIgnoreList !=null)
        ignore = new List<string>(strIgnoreList);
    //Loop thru the Column in the DataRow
    for (int intcolcount = 0; 
         intcolcount <= objSource.ItemArray.Length - 1; intcolcount++)
    {
        //Get the column name 
        string strPropertyName = objSource.Table.Columns[intcolcount].ColumnName;
        if (ignore==null || (ignore != null && !ignore.Contains(strPropertyName)))
        {
            try
            {
                //set the value to the object
                SetPropertyValue(objTarget, strPropertyName, 
                                 objSource[strPropertyName]);
            }
            catch (Exception ex)
            {
                if (!blnSuppressExceptions)
                    throw new Exception(string.Format("Error while " + 
                          "assigning value to the property : {0}", 
                          strPropertyName), ex);

            }
        }
    }
}
#endregion

SetPropertyValue method

Actually, only the exact mapping and setting of values to the properties takes place here.

  • Source DataRow's column name, value of the column, and the target object are taken as input.
  • Get the property with that column name in the class.
  • Get the property's type (data type).
  • Get the value type (value of that column).
  • If the value is null, then set the property's value as Dbnull.
  • If the value is not null, then compare the property's type with the value's type.
  • If property's type matches the value's type, then copy the value to the property of the object.
  • If it doesn't match, try to coerce and set to the property.
#region SetPropertyValue
/// <summary>
/// Sets an object's property with the specified value,
/// coercing that value to the appropriate type if possible.
/// </summary>
/// <param name="target">Object containing the property to set.</param>
/// <param name="propertyName">Name of the property to set.</param>
/// <param name="value">Value to set into the property.</param>

public static void SetPropertyValue(object objTarget, 
              string strPropertyName, object objValue)
{
    //Get the property of the Target object(Class)
    // using the Column Name from the Datarow/DataReader
    PropertyInfo propertyInfo =
        objTarget.GetType().GetProperty(strPropertyName);

    //Get the Type (datatype)of the Property of the target class
    Type pType = GetPropertyType(propertyInfo.PropertyType);
    //Get the Type (datatype) of the Value from the Datarow/DataReader
    Type vType = GetPropertyType(objValue.GetType());

    //For null values in the Datarow/DataReader set the property value as DBnull 
    if (objValue == null)
        propertyInfo.SetValue(objTarget, DBNull.Value, null);
    else if (objValue.Equals(System.DBNull.Value))
    {
        if (pType.Equals(typeof(System.DateTime)))
            propertyInfo.SetValue(objTarget, DateTime.MinValue, null);
        else if (pType.Equals(typeof(System.Double)))
            propertyInfo.SetValue(objTarget, 0, null);
        else if (pType.Equals(typeof(System.Int32)))
            propertyInfo.SetValue(objTarget, 0, null);
        else if (pType.Equals(typeof(System.Byte[])))
            propertyInfo.SetValue(objTarget, new byte[0], null);
    }
    else
    {
        //For not null values in the Datarow/DataReader 
        if (pType.Equals(vType))
        {
            // types match, just copy value
            propertyInfo.SetValue(objTarget, objValue, null);
        }
        else
        {
            // types don't match, try to coerce
            if (pType.Equals(typeof(Guid)))
                propertyInfo.SetValue(objTarget, new Guid(objValue.ToString()), null);
            else if (pType.IsEnum && vType.Equals(typeof(string)))
                propertyInfo.SetValue(objTarget, 
                        Enum.Parse(pType, objValue.ToString()), null);
            else
                propertyInfo.SetValue(objTarget, 
                        Convert.ChangeType(objValue, pType), null);
        }
    }
}

Conclusion

This will help in making your business layer more generic.

Note: The property name of the class should match either the column name or the alias name in the SQL Select statement.

License

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

About the Author

Anandhi K

Architect

India India

Member



Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
Generalcode in VB.NET PinmemberD.Sridhar5:48 8 May '07  
GeneralRe: code in VB.NET Pinmemberkarthika_rani0:08 9 May '07  
aspalliance.com provides the translator to convert C# to VB.Net. Try it.
GeneralRe: code in VB.NET PinmemberD.Sridhar3:12 9 May '07  

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

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.5.120529.1 | Last Updated 7 May 2007
Article Copyright 2007 by Anandhi K
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid