Friday, October 28, 2011

Dynamically inject values to object using reflection in type safe manner - DataAccess Layer Map Function


I am sure most of us, who are not using any ORM, have written methods like the following number of times in our life. This is the function that maps the resultant DataGrid with the equivalent object.

protected override UserSession Map(System.Data.IDataReader reader)
{
  var userSession = new UserSession()
{
  ID = NullHandler.GetLong(reader, "ID"),
 Browser = NullHandler.GetString(reader, "Browser"),
 BrowserVersion = NullHandler.GetString(reader, "BrowserVersion"),
 IpAddress = NullHandler.GetString(reader, "IpAddress"),
 UserAgent = NullHandler.GetString(reader, "UserAgent"),
 SiteId = NullHandler.GetInt32(reader, "SiteId"),
 StartTimeStamp = NullHandler.GetDateTime(reader, "StartTimeStamp"),
 EndTimeStamp = NullHandler.GetDateTime(reader, "EndTimeStamp"),
 UserName = NullHandler.GetString(reader, "UserName"),
 IsActive = NullHandler.GetBoolean(reader, "IsActive"),
 IsDeleted = NullHandler.GetBoolean(reader, "IsDeleted"),
 CreatedBy = NullHandler.GetString(reader, "CreatedBy"),
 CreatedByDateTime = NullHandler.GetDateTime(reader, "CreateByDateTime"),
 LastModifiedBy = NullHandler.GetString(reader, "LastModifiedBy"),
 LastModifiedByDateTime = NullHandler.GetDateTime(reader, "LastModifiedByDateTime"),
 SystemModstamp = NullHandler.GetDateTime(reader, "SystemModstamp")
};


  return userSession;
}

What most of us do to write this function is, we copy one similar function from some other class then, make changes to the function based on our entity. Whatever time this function takes to write, I am sure we all feel bore to do that.

Most of the time,  we always override this map function in every DataAccess class even though this function has a similar pattern of implementatiion.

That is why, I have tried to write a default implementation of this Map function in BaseDataAccess to save our time:

/// Maps and sets the Entity Model from the specified reader.protected virtual TEntity Map(IDataReader reader)
{
var entity = Activator.CreateInstance();
  foreach (PropertyInfo info in entity.GetType().GetProperties()
.Where(p => p.GetCustomAttributes(true)
 .FirstOrDefault(at => at.GetType() == typeof (ParameterExclusionAttribute)) == null)
)
  {
object data = NullHandler.GetDataForColumn(info.Name, reader);
if (info.CanRead)
{
 Type propertyType = info.PropertyType;
 Type underlyingType = Nullable.GetUnderlyingType(propertyType);


 if(data==null || data == DBNull.Value)
 {
if (propertyType == typeof(String))
{
 data = string.Empty;
}
else
{
 data = propertyType.IsValueType ? Activator.CreateInstance(propertyType) : null;
}                                
 }
 else
 {
data = Convert.ChangeType(
 data, underlyingType ?? propertyType);                                
 }
 info.SetValue(entity, data, null);
}                    
  }
return entity;
}

This Map function in BaseDataAccess will automatically map the resultant datareader with your entity. So, it will save our time and energy and we will not need to write this Map functions in every DAL classe anymore. BUT, There is a constraint. To use this default implementation we must follow the practice of “Convention over Configuration”. To  use this, we must have the name of the object’s properties and the database table’s column name same.  I believe this is a very standard practice and we all follow this. Who are not following this yet, should start doing so from now.

No comments:

Post a Comment