Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a generic retrieval method to return 1 record

I am working through a code sample, and I just want to hear some opinions on the way that they have done things. They use plain old ADO.NET. They have a generic function called Read that brings back 1 record. Here is the code:

public static T Read<T>(string storedProcedure, Func<IDataReader, T> make, object[] parms = null)
{
   using (SqlConnection connection = new SqlConnection())
   {
      connection.ConnectionString = connectionString;

      using (SqlCommand command = new SqlCommand())
      {
         command.Connection = connection;
         command.CommandType = CommandType.StoredProcedure;
         command.CommandText = storedProcedure;
         command.SetParameters(parms);

         connection.Open();

         T t = default(T);
         var reader = command.ExecuteReader();
         if (reader.Read())
            t = make(reader);

         return t;
      }
   }
}

I don't know why:

  • They use Func<IDataReader, T> make as part of the method signature? Is it efficient to do it like this? Is there a better way/best practice to do this?
  • I don't understand T t = default(T);? Is it efficient to do it like this? Is there a better way/best practice to do this?
  • What does t = make(reader); do? Is it efficient to do it like this? Is there a better way/best practice to do this?

The calling function would look something like this:

public Customer GetCustomer(int customerId)
{
   // Other code here
   string storedProcedure = "MyStoredProcedure";
   object[] parameters = { "@CustomerId", customerId };
   return Db.Read(storedProcedure, Make, parameters);
}

private static Func<IDataReader, Customer> Make = reader =>
new Customer
{
   CustomerId = reader["CustomerId"].AsId(),
   Company = reader["CompanyName"].AsString(),
   City = reader["City"].AsString
};

I don't understand the Func Make part? Can someone please explain to me what is happening here and if this is good practices. Any changes would be appreciated, but please provide detailed sample code :)

like image 705
Brendan Vogt Avatar asked Jun 08 '11 06:06

Brendan Vogt


1 Answers

The delegate for the make (materialization) is pretty versatile and flexible, but IMO makes for a bit of unnecessary work in the vast majority of cases. In terms of what it does - they use the delegate as a callback to get the caller to specify how to read the record.

Note, since they don't expect the consumer to change record, they should probably be exposing IDataRecord, not IDataReader (any reader also implements record).

Note that if there are any error messages in the TDS stream after the first record, the approach shown won't see them - but that is an edge case. If you wanted to mitigate against that, you could read to the end of the TDS stream:

while(reader.NextResult()) {}

Personally, though, I'd just use dapper-dot-net here - avoids having to write that per-type code manually:

var cust = connection.Query<Customer>("MyStoredProcedure",
     new { CustomerId = customerId },
     commandType: CommandType.StoredProcedure).Single();

this executes MyStoredProcedure as a sproc, passing in @CustomerId with the value from customerId, then applies a direct column<===>property/field match to create Customer records, then asserts that there is exactly one result - and returns it.

like image 104
Marc Gravell Avatar answered Sep 30 '22 13:09

Marc Gravell