Fastest way to map result of SqlDataReader to object

后端 未结 11 1074
花落未央
花落未央 2021-02-04 08:49

I\'m comparing materialize time between Dapper and ADO.NET and Dapper. Ultimately, Dapper tend to faster than ADO.NET, though the first time a given fetch query was executed

相关标签:
11条回答
  • 2021-02-04 09:00
    List<T> result = new List<T>();
    SqlDataReader reader = com.ExecuteReader();
    
    while(reader.Read())
    {                        
        Type type = typeof(T);
        T obj = (T)Activator.CreateInstance(type);
        PropertyInfo[] properties = type.GetProperties();
    
        foreach (PropertyInfo property in properties)
        {
            try
            {
                var value = reader[property.Name];
                if (value != null)
                    property.SetValue(obj, Convert.ChangeType(value.ToString(), property.PropertyType));
            }
            catch{}                            
        }
        result.Add(obj);
    }
    
    0 讨论(0)
  • 2021-02-04 09:01

    This kinda works

     public static object PopulateClass(object o, SQLiteDataReader dr, Type T)
        {
            Type type = o.GetType();
            PropertyInfo[] properties = type.GetProperties();
    
            foreach (PropertyInfo property in properties)
            {
                T.GetProperty(property.Name).SetValue(o, dr[property.Name],null);
            }
            return o;
        }
    

    Note I'm using SQlite here but the concept is the same. As an example I'm filling a Game object by calling the above like this-

    g = PopulateClass(g, dr, typeof(Game)) as Game;
    

    Note you have to have your class match up with datareader 100%, so adjust your query to suit or pass in some sort of list to skip fields. With a SQLDataReader talking to a SQL Server DB you have a pretty good type match between .net and the database. With SQLite you have to declare your ints in your class as Int64s for this to work and watch sending nulls to strings. But the above concept seems to work so it should get you going. I think this is what the Op was after.

    0 讨论(0)
  • 2021-02-04 09:05

    I love that the most upvoted answer mentions @MarkGravel and his FastMember. But if you're already using Dapper, which is also a component of his, you can use Dapper's GetRowParser like this:

    var parser = reader.GetRowParser<MyObject>(typeof(MyObject));
    
    while (reader.Read())
    {
        var myObject = parser(reader);
    }
    
    0 讨论(0)
  • 2021-02-04 09:08

    There is a SqlDataReader Mapper library in NuGet which helps you to map SqlDataReader to an object. Here is how it can be used (from GitHub documentation):

    var mappedObject = new SqlDataReaderMapper<DTOObject>(reader)
        .Build();
    

    Or, if you want a more advanced mapping:

    var mappedObject = new SqlDataReaderMapper<DTOObject>(reader)
         .NameTransformers("_", "")
         .ForMember<int>("CurrencyId")
         .ForMember("CurrencyCode", "Code")
         .ForMember<string>("CreatedByUser", "User").Trim()
         .ForMemberManual("CountryCode", val => val.ToString().Substring(0, 10))
         .ForMemberManual("ZipCode", val => val.ToString().Substring(0, 5), "ZIP")
         .Build();
    

    Advanced mapping allows you to use name transformers, change types, map fields manually or even apply functions to the object's data so that you can easily map objects even if they differ with a reader.

    0 讨论(0)
  • 2021-02-04 09:15

    Here's a way to make your ADO.NET code faster.

    When you do your select, list out the fields that you are selecting rather than using select *. This will let you ensure the order that the fields are coming back even if that order changes in the database.Then when getting those fields from the Reader, get them by index rather than by name. Using and index is faster.

    Also, I'd recommend not making string database fields nullable unless there is a strong business reason. Then just store a blank string in the database if there is no value. Finally I'd recommend using the Get methods on the DataReader to get your fields in the type they are so that casting isn't needed in your code. So for example instead of casting the DataReader[index++] value as an int use DataReader.GetInt(index++)

    So for example, this code:

     salesOrderHeader = new SalesOrderHeaderSQLserver();
     salesOrderHeader.SalesOrderId = (int)reader["SalesOrderId"];
     salesOrderHeader.SalesOrderNumber =       reader["SalesOrderNumber"] as string;
     salesOrderHeader.AccountNumber = reader["AccountNumber"] as string;
    

    becomes

     int index = 0;
     salesOrderHeader = new SalesOrderHeaderSQLserver();
     salesOrderHeader.SalesOrderId = reader.GetInt(index++);
     salesOrderHeader.SalesOrderNumber = reader.GetString(index++);
     salesOrderHeader.AccountNumber = reader.GetString(index++);
    

    Give that a whirl and see how it does for you.

    0 讨论(0)
提交回复
热议问题