I have following code that's capable of mapping Reader
to simple objects. The trouble is in case the object is composite it fails to map. I am not able to perform recursion by checking the property if it is a class itself
prop.PropertyType.IsClass
as Type is required to call DataReaderMapper()
. Any idea on how this may be achieved or some other approach? Also, currently I am not wishing to use any ORM.
public static class MapperHelper
{
/// <summary>
/// extension Method for Reader :Maps reader to type defined
/// </summary>
/// <typeparam name="T">Generic type:Model Class Type</typeparam>
/// <param name="dataReader">this :current Reader</param>
/// <returns>List of Objects</returns>
public static IEnumerable<T> DataReaderMapper<T>(this IDataReader dataReader)where T : class, new()
{
T obj = default(T);
//optimized taken out of both foreach and while loop
PropertyInfo[] PropertyInfo;
var temp = typeof(T);
PropertyInfo = temp.GetProperties();
while (dataReader.Read())
{
obj = new T();
foreach (PropertyInfo prop in PropertyInfo)
{
if (DataConverterHelper.ColumnExists(dataReader,prop.Name) && !dataReader.IsDBNull(prop.Name))
{
prop.SetValue(obj, dataReader[prop.Name], null);
}
}
yield return obj;
}
}
}
Don't make DataReaderMapper
recursive. Just make the mapping part recursive:
static void Assign(IDataReader reader, object instance) {
foreach (PropertyInfo prop in PropertyInfo)
{
if (IsValue(prop))
{
prop.SetValue(obj, dataReader[prop.Name], null);
}
else if (IsClass(prop)) {
var subInstance = Activator.CreateInstance(prop.PropertyType);
prop.SetValue(obj, subInstance, null);
Assign(subInstance, dataReader);
}
}
}
Like that. This recursively initializes all class type properties with default constructed instances and assigns data reader values to them.
The code is clearly simplified. I elided some of your stuff and IsValue
/IsClass
are left to implement to your liking. Also, you probably want to use a naming scheme so that a.b.c
as a column name maps to that property. That's doable by passing the current name prefix as a parameter to Assign
.
Further note, that DataReaderMapper
being generic isn't required. I'm saying this because you struggled with that. Replace typeof(T)
with a Type
parameter and return an IEnumerable<object>
. Then call Cast<T>()
on the result of your method. So you see that this algorithm can in principle work without generics.