Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I have implemented the code to convert datareader to list of objects. My intention was to not to use column name string literals while reading the datareader.

public static IEnumerable<T> GetListFromDataReader<T>(IDataReader reader) where T : new()
{
    var properties = typeof(T).GetProperties();

    var modelProperties = new List<string>();
    var columnList = (reader.GetSchemaTable().Select()).Select(r => r.ItemArray[0].ToString());
    while (reader.Read())
    {
        var element = Activator.CreateInstance<T>();
        Dictionary<string, string> dbMappings = DBColumn(element);
        string columnName;
        foreach (var f in properties)
        {

            if (!columnList.Contains(f.Name) && !dbMappings.ContainsKey(f.Name))
                continue;
            columnName = dbMappings.ContainsKey(f.Name) ? dbMappings[f.Name] : f.Name;
            var o = (object)reader[columnName];

            if (o.GetType() != typeof(DBNull)) f.SetValue(element, ChangeType(o, f.PropertyType), null);
        }
        yield return element;
    }

}

public static object ChangeType(object value, Type conversion)
{
    var t = conversion;

    if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
    {
        if (value == null)
        {
            return null;
        }

        t = Nullable.GetUnderlyingType(t); ;
    }

    return Convert.ChangeType(value, t);
}

public static Dictionary<string,string> DBColumn<T>(T item) where T:new()
{

    Dictionary<string, string> dbMappings = new Dictionary<string, string>();
    var type = item.GetType();
    var properties = type.GetProperties();
    foreach (var property in properties)
    {
        var attributes = property.GetCustomAttributes(false);
        var columnMapping = attributes
    .FirstOrDefault(a => a.GetType() == typeof(DbColumnAttribute));
        if (columnMapping != null)
        {
            dbMappings.Add(property.Name, ((DbColumnAttribute)columnMapping).Name);
        }
    }
    return dbMappings;
}

Can somebody help me by providing a review? I am doubtful of too much usage of reflections in loops would downgrade the performance. Or should I compromise and better to go with string literals itself while reading?

share|improve this question

1 Answer 1

up vote 1 down vote accepted

<clippy>It appears you are writing an Object/Relational Mapper! Would you like some help?</clippy>

Yes, a lot of reflection in loops can be bad thing since reflection is known for its slowness. In a situation like this, it's best to cache the results as your types will not be changing during the lifetime of the program. Simple fix:

    private static readonly IDictionary<Type, IDictionary<string, string>> typeMappings =
        new Dictionary<Type, IDictionary<string, string>>();

    public static IDictionary<string, string> DBColumn<T>() where T : new()
    {
        var type = typeof(T);
        IDictionary<string, string> databaseMappings;

        if (typeMappings.TryGetValue(type, out databaseMappings))
        {
            return databaseMappings;
        }

        databaseMappings = new Dictionary<string, string>();
        foreach (var property in type.GetProperties())
        {
            var columnMapping = property
                .GetCustomAttributes(false)
                .FirstOrDefault(attribute => attribute is DbColumnAttribute);

            if (columnMapping != null)
            {
                databaseMappings.Add(property.Name, ((DbColumnAttribute)columnMapping).Name);
            }
        }

        typeMappings.Add(type, databaseMappings);
        return databaseMappings;
    }

You'll notice it uses a static dictionary to hold the mappings between the type and the data column mappings, so it should only be done once per type. That'll be your performance boost right there. You'll only take the hit the first time through and not after that.

Also, I did change some variable names and coded to interfaces (IDictionary instead of Dictionary) and take note that the method doesn't need a parameter any more: the T generic type parameter gives me the type rather than doing .GetType() on the parameter.

Hope this helps you!

share|improve this answer
    
Hi Jesse C. Slicer: Appreciate for you time. Can you suggest me, whether this would be good practice to retrieve data from the datareader through above fashion, where the project is involved lot of CRUD ops. –  gee'K'iran Jul 30 at 6:48
1  
Personally, I'd forego any usage of DataReader and any other ADO.NET operations and use a time-tested O/RM such as Entity Framework, NHibernate or ORMLite. It does what you're looking to do and lots more. –  Jesse C. Slicer Jul 30 at 13:23
    
Thanks Jesse. I am also a fan of ORMs and thats the reason I am trying to avoid reading the datareader values manually by passing string keys. However, the application, which I started working recently is an ongoing one since 2 years and we are not at the place where we can revamp the whole Ado.Net stuff. –  gee'K'iran Jul 31 at 7:34

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.