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:
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?T t = default(T);
? Is it efficient to do it like this? Is there a better way/best practice to do this?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 :)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With