Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fastest way to map result of SqlDataReader to object

Tags:

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 is slower than ADO.NET. a few result show that Dapper a little bit faster than ADO.NET(almost all of result show that it comparable though)
So I think I'm using inefficient approach to map result of SqlDataReader to object.
This is my code

var sql = "SELECT * FROM Sales.SalesOrderHeader WHERE SalesOrderID = @Id";         var conn = new SqlConnection(ConnectionString);         var stopWatch = new Stopwatch();          try         {             conn.Open();             var sqlCmd = new SqlCommand(sql, conn);              for (var i = 0; i < keys.GetLength(0); i++)             {                 for (var r = 0; r < keys.GetLength(1); r++)                 {                     stopWatch.Restart();                     sqlCmd.Parameters.Clear();                     sqlCmd.Parameters.AddWithValue("@Id", keys[i, r]);                     var reader = await sqlCmd.ExecuteReaderAsync();                     SalesOrderHeaderSQLserver salesOrderHeader = null;                      while (await reader.ReadAsync())                     {                         salesOrderHeader = new SalesOrderHeaderSQLserver();                         salesOrderHeader.SalesOrderId = (int)reader["SalesOrderId"];                         salesOrderHeader.SalesOrderNumber = reader["SalesOrderNumber"] as string;                         salesOrderHeader.AccountNumber = reader["AccountNumber"] as string;                         salesOrderHeader.BillToAddressID = (int)reader["BillToAddressID"];                         salesOrderHeader.TotalDue = (decimal)reader["TotalDue"];                         salesOrderHeader.Comment = reader["Comment"] as string;                         salesOrderHeader.DueDate = (DateTime)reader["DueDate"];                         salesOrderHeader.CurrencyRateID = reader["CurrencyRateID"] as int?;                         salesOrderHeader.CustomerID = (int)reader["CustomerID"];                         salesOrderHeader.SalesPersonID = reader["SalesPersonID"] as int?;                         salesOrderHeader.CreditCardApprovalCode = reader["CreditCardApprovalCode"] as string;                         salesOrderHeader.ShipDate = reader["ShipDate"] as DateTime?;                         salesOrderHeader.Freight = (decimal)reader["Freight"];                         salesOrderHeader.ModifiedDate = (DateTime)reader["ModifiedDate"];                         salesOrderHeader.OrderDate = (DateTime)reader["OrderDate"];                         salesOrderHeader.TerritoryID = reader["TerritoryID"] as int?;                         salesOrderHeader.CreditCardID = reader["CreditCardID"] as int?;                         salesOrderHeader.OnlineOrderFlag = (bool)reader["OnlineOrderFlag"];                         salesOrderHeader.PurchaseOrderNumber = reader["PurchaseOrderNumber"] as string;                         salesOrderHeader.RevisionNumber = (byte)reader["RevisionNumber"];                         salesOrderHeader.Rowguid = (Guid)reader["Rowguid"];                         salesOrderHeader.ShipMethodID = (int)reader["ShipMethodID"];                         salesOrderHeader.ShipToAddressID = (int)reader["ShipToAddressID"];                         salesOrderHeader.Status = (byte)reader["Status"];                         salesOrderHeader.SubTotal = (decimal)reader["SubTotal"];                         salesOrderHeader.TaxAmt = (decimal)reader["TaxAmt"];                     }                      stopWatch.Stop();                     reader.Close();                     await PrintTestFindByPKReport(stopWatch.ElapsedMilliseconds, salesOrderHeader.SalesOrderId.ToString());                 } 

I used as keyword to cast in nullable column, is that correct?
and this is code for Dapper.

using (var conn = new SqlConnection(ConnectionString))         {             conn.Open();             var stopWatch = new Stopwatch();              for (var i = 0; i < keys.GetLength(0); i++)             {                 for (var r = 0; r < keys.GetLength(1); r++)                 {                     stopWatch.Restart();                     var result = (await conn.QueryAsync<SalesOrderHeader>("SELECT * FROM Sales.SalesOrderHeader WHERE SalesOrderID = @Id", new { Id = keys[i, r] })).FirstOrDefault();                     stopWatch.Stop();                     await PrintTestFindByPKReport(stopWatch.ElapsedMilliseconds, result.ToString());                 }             }         } 
like image 234
witoong623 Avatar asked Dec 08 '16 13:12

witoong623


People also ask

Is there anything faster than SqlDataReader in net?

Caching namespace. If you're doing purely data operations (as your question suggests), you could rewrite your code which is using the data to be T-SQL and run natively on SQL. This has the potential to be much faster, as you will be working with the data directly and not shifting it about.

How do I get data from ExecuteReader?

To retrieve data using a DataReader, create an instance of the Command object, and then create a DataReader by calling Command. ExecuteReader to retrieve rows from a data source.

Is DataReader faster than DataSet?

A great plus about DataSet is that it can be filled using multiple data sources. The DataSet represents a complete set of data among the tables that include related tables, constraints, and relationships. However, this greater functionality comes with performance overhead; therefore, DataSet is slower than DataReader.

Does SqlDataReader close connection?

The SqlConnection is closed automatically at the end of the using code block.


2 Answers

When in doubt regarding anything db or reflection, I ask myself, "what would Marc Gravell do?".

In this case, he would use FastMember! And you should too. It's the underpinning to the data conversions in Dapper, and can easily be used to map your own DataReader to an object (should you not want to use Dapper).

Below is an extension method converting a SqlDataReader into something of type T:

PLEASE NOTE: This code implies a dependency on FastMember and is written for .NET Core (though could easily be converted to .NET Framework/Standard compliant code).

public static T ConvertToObject<T>(this SqlDataReader rd) where T : class, new() {     Type type = typeof(T);     var accessor = TypeAccessor.Create(type);     var members = accessor.GetMembers();     var t = new T();      for (int i = 0; i < rd.FieldCount; i++)     {         if (!rd.IsDBNull(i))         {             string fieldName = rd.GetName(i);              if (members.Any(m => string.Equals(m.Name, fieldName, StringComparison.OrdinalIgnoreCase)))             {                 accessor[t, fieldName] = rd.GetValue(i);             }         }     }      return t; } 
like image 61
pim Avatar answered Sep 20 '22 22:09

pim


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); } 
like image 28
Alex from Jitbit Avatar answered Sep 20 '22 22:09

Alex from Jitbit