After connecting to the database, can I get the name of all the columns that were returned in my SqlDataReader?
12 Answers
var reader = cmd.ExecuteReader();
var columns = new List<string>();
for(int i=0;i<reader.FieldCount;i++)
{
columns.Add(reader.GetName(i));
}
or
var columns = Enumerable.Range(0, reader.FieldCount).Select(reader.GetName).ToList();
8 Comments
columns = Enumerable.Range(0, reader.FieldCount) .Select(reader.GetName).ToList();SELECT id AS "MyId" FROM table;There is a GetName function on the SqlDataReader which accepts the column index and returns the name of the column.
Conversely, there is a GetOrdinal which takes in a column name and returns the column index.
2 Comments
GetOrdinal was perfect. I was looking for GetName, but much cleaner solution for my issue with GetOrdinal.You can get the column names from a DataReader.
Here is the important part:
for (int col = 0; col < SqlReader.FieldCount; col++)
{
Console.Write(SqlReader.GetName(col).ToString()); // Gets the column name
Console.Write(SqlReader.GetFieldType(col).ToString()); // Gets the column type
Console.Write(SqlReader.GetDataTypeName(col).ToString()); // Gets the column database type
}
Comments
Already mentioned. Just a LINQ answer:
var columns = reader.GetSchemaTable().Rows
.Cast<DataRow>()
.Select(r => (string)r["ColumnName"])
.ToList();
//Or
var columns = Enumerable.Range(0, reader.FieldCount)
.Select(reader.GetName)
.ToList();
The second one is cleaner and much faster. Even if you cache GetSchemaTable in the first approach, the querying is going to be very slow.
7 Comments
reader.Cast<IDataRecord>().ToList(). I believe you could use dynamic keyword there instead of IDataRecord but with no benefit. DataTable was designed to ease onetime loading, so you could use that too but you lose the benefit of loading on demand (with data reader you could stop loading at any point), like var dt = new DataTable(); dt.Load(reader); return dt.AsEnumerable().ToList();. There are many libraries which can automate this for you, find them here stackoverflow.com/questions/11988441 and here stackoverflow.com/questions/1464883reader.Cast<IEnumerable<dynamic>> and .Cast<dynamic>, but it says, Cannot convert method group 'Cast' to non-delegate type 'dynamic'. Did you intend to invoke the method? what did I do wrong there? (I looked at your sources, but they required you to know the column name, which I don't)If you want the column names only, you can do:
List<string> columns = new List<string>();
using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SchemaOnly))
{
DataTable dt = reader.GetSchemaTable();
foreach (DataRow row in dt.Rows)
{
columns.Add(row.Field<String>("ColumnName"));
}
}
But if you only need one row, I like my AdoHelper addition. This addition is great if you have a single line query and you don't want to deal with data table in you code. It's returning a case insensitive dictionary of column names and values.
public static Dictionary<string, string> ExecuteCaseInsensitiveDictionary(string query, string connectionString, Dictionary<string, string> queryParams = null)
{
Dictionary<string, string> CaseInsensitiveDictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
try
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = query;
// Add the parameters for the SelectCommand.
if (queryParams != null)
foreach (var param in queryParams)
cmd.Parameters.AddWithValue(param.Key, param.Value);
using (SqlDataReader reader = cmd.ExecuteReader())
{
DataTable dt = new DataTable();
dt.Load(reader);
foreach (DataRow row in dt.Rows)
{
foreach (DataColumn column in dt.Columns)
{
CaseInsensitiveDictionary.Add(column.ColumnName, row[column].ToString());
}
}
}
}
conn.Close();
}
}
catch (Exception ex)
{
throw ex;
}
return CaseInsensitiveDictionary;
}
3 Comments
throw ex; is a worst practice.I use the GetSchemaTable method, which is exposed via the IDataReader interface.
1 Comment
You sure can.
protected void GetColumNames_DataReader()
{
System.Data.SqlClient.SqlConnection SqlCon = new System.Data.SqlClient.SqlConnection("server=localhost;database=northwind;trusted_connection=true");
System.Data.SqlClient.SqlCommand SqlCmd = new System.Data.SqlClient.SqlCommand("SELECT * FROM Products", SqlCon);
SqlCon.Open();
System.Data.SqlClient.SqlDataReader SqlReader = SqlCmd.ExecuteReader();
System.Int32 _columncount = SqlReader.FieldCount;
System.Web.HttpContext.Current.Response.Write("SqlDataReader Columns");
System.Web.HttpContext.Current.Response.Write(" ");
for ( System.Int32 iCol = 0; iCol < _columncount; iCol ++ )
{
System.Web.HttpContext.Current.Response.Write("Column " + iCol.ToString() + ": ");
System.Web.HttpContext.Current.Response.Write(SqlReader.GetName( iCol ).ToString());
System.Web.HttpContext.Current.Response.Write(" ");
}
}
This is originally from: http://www.dotnetjunkies.ddj.com/Article/B82A22D1-8437-4C7A-B6AA-C6C9BE9DB8A6.dcik
Comments
Odd that for such an old question no one proposed using the DbDataReaderExtensions.GetColumnSchema(DbDataReader) Method:
var colNames = reader.GetColumnSchema().Select((dbCol) => dbCol.ColumnName);
var colNames = (await reader.GetColumnSchemaAsync()).Select((col) => col.ColumnName);
Comments
This is a tweak of the more popular answers, with arguably a couple minor improvements:
var columnNames = new HashSet<string>(
Enumerable.Range(0, reader.FieldCount).Select(reader.GetName),
StringComparer.OrdinalIgnoreCase);
To check for TaskId (or taskid, taskID, TaskID, etc. -- works for all of them):
if (columnNames.Contains("TaskId")) {
// yes, the reader has this column name...
}
Contrast to other answers:
- Column check is not case sensitive.
- Column check syntax is no less readable.
- Using a
HashSetis faster, at least on paper:O(log n)as opposed toO(n). This probably won't matter if there are just a handful of columns.