Instances of SqlDataReader
can be indexed by both integers and strings (see below: reader[col]
). So, I want the wrapping function to accept both List<string>
and List<int>
as an argument. I really don't want type-checking at runtime, though, and, as C# doesn't have something like type unions, I assume overloading is the best way to allow a function to accept different types without involving generics and at-runtime type-checking.
So... I can easily just copy the method with List<int>
instead of List<string>
and overload it that way, but I don't want (and shouldn't) duplicate code like this. There must be a better way.
public List<List<object>> Query(string query, List<string> relevantColumns)
{
var rows = new List<List<object>>();
using (SqlConnection connection =
new SqlConnection(this.ConnectionString))
{
var command = new SqlCommand(query, connection);
command.Connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
List<object> row = (from col
in relevantColumns
select reader[col]).ToList();
rows.Add(row);
}
}
}
return rows;
}
CodePudding user response:
You could have a third overload which takes a Func<SqlDataReader, List<object>>
. Then call the other two overloads in terms of that overload. Something like...
public static class SqlUtilities
{
public static List<List<object>> Query(string query, List<string> relevantColumns)
{
return Query(query, reader => (from col in relevantColumns select reader[col]).ToList());
}
public static List<List<object>> Query(string query, List<int> relevantColumns)
{
return Query(query, reader => (from col in relevantColumns select reader[col]).ToList());
}
private static List<List<object>> Query(
string query,
Func<SqlDataReader, List<object>> selector)
{
var rows = new List<List<object>>();
using (SqlConnection connection =
new SqlConnection(this.ConnectionString))
{
var command = new SqlCommand(query, connection);
command.Connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
List<object> row = selector(reader);
rows.Add(row);
}
}
}
return rows;
}
}
In this specific case, however, my opinion is that a better way to solve it would be to rewrite query
if that is an option. relevantColumns
as a list of names or a list of ordinals is unnecessary if query
is only selecting the relevant columns.