Можете ли вы получить имена столбцов из SqlDataReader?

276

Могу ли я получить имя всех столбцов, которые были возвращены в моем после подключения к базе данных SqlDataReader?

Бланкмэн
источник

Ответы:

460
var reader = cmd.ExecuteReader();

var columns = new List<string>();

for(int i=0;i<reader.FieldCount;i++)
{
   columns.Add(reader.GetName(i));
}

или

var columns = Enumerable.Range(0, reader.FieldCount).Select(reader.GetName).ToList();
Роб Стивенсон-Леггетт
источник
71
это безумие, что нет перечисляемого интерфейса, который позволял бы перебирать столбцы
JohnFx
61
Немного короче:columns = Enumerable.Range(0, reader.FieldCount) .Select(reader.GetName).ToList();
Алекс
2
Это прекрасно работает. Я также узнал, что все имена моих столбцов были заглавными, если я не использовал кавычки вокруг имени столбца. SELECT id AS "MyId" FROM table;
Styfle
сэр, он возвращает все имена столбцов в нижнем регистре. Имена столбцов в таблице все в верхнем регистре, как OBJECTID, и читатель возвращает строчные как objectid
Muneem Habib
2
его столбцы Dim () As String = Enumerable.Range (0, cTab.FieldCount) .Select (Function (n) cTab.GetName (n)). ToArray
swe
77

Существует GetNameфункция, SqlDataReaderкоторая принимает индекс столбца и возвращает имя столбца.

И наоборот, есть, GetOrdinalкоторый принимает имя столбца и возвращает индекс столбца.

Стивен Райтон
источник
3
Две причины: во-первых, оригинальный постер еще не выбрал ответ, и, во-вторых, есть другие ответы, которые дают более подробное описание «решения» проблемы, а не только наличие функциональности. Лично мне больше всего нравится ответ Стивена Лайонса, поскольку он не только говорит о GetName, но и затрагивает FieldType и DataType.
Стивен Райтон
1
GetOrdinalбыло прекрасно. Я искал GetName, но гораздо более чистое решение для моей проблемы с GetOrdinal.
Goodeye
43

Вы можете получить имена столбцов из DataReader.

Вот важная часть:

  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
  }
Стивен Лайонс
источник
15

Уже упоминалось. Просто ответ LINQ :

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();

Второй чище и намного быстрее. Даже если вы кешируете GetSchemaTableпри первом подходе, запросы будут очень медленными.

Навфал
источник
Есть ли способ сделать это с помощью значений?
Трэвис Хитер
@TravisHeeter Я не понимаю тебя. Найти имена столбцов по значениям чего?
nawfal
Я имею в виду просто восточный способ получить значения в результирующем наборе в список, или, возможно, все это для объекта IEnumerable <dynamic>.
Трэвис Хитер
@TravisHeeter да может сделать reader.Cast<IDataRecord>().ToList(). Я считаю, что вы могли бы использовать dynamicключевое слово там, IDataRecordно без пользы. DataTableбыл разработан, чтобы облегчить однократную загрузку, так что вы можете использовать это тоже, но вы теряете преимущество загрузки по требованию (с помощью считывателя данных вы можете прекратить загрузку в любой момент), например var dt = new DataTable(); dt.Load(reader); return dt.AsEnumerable().ToList();. Есть много библиотек, которые могут автоматизировать это для вас, найдите их здесь stackoverflow.com/questions/11988441 и здесь stackoverflow.com/questions/1464883
nawfal
Я пробовал reader.Cast<IEnumerable<dynamic>>и .Cast<dynamic>, но там написано, Cannot convert method group 'Cast' to non-delegate type 'dynamic'. Did you intend to invoke the method?что я там не так сделал? (Я посмотрел на ваши источники, но они потребовали, чтобы вы знали имя столбца, а я нет)
Трэвис Хитер
6

Если вы хотите только имена столбцов, вы можете сделать:

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"));
    }
}

Но если вам нужен только один ряд, мне нравится мое дополнение AdoHelper. Это дополнение хорошо, если у вас есть однострочный запрос, и вы не хотите иметь дело с таблицей данных в своем коде. Он возвращает нечувствительный к регистру словарь имен и значений столбцов.

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;
}
Усадьба Якир
источник
1
throw ex;это худшая практика.
asawyer
2
Это просто пример
усадьба Якира
5
Asawyer, ты должен хотя бы сказать почему. Я предполагаю, что вы собираетесь сказать, что вы должны использовать "throw;" вместо этого, чтобы вы не потеряли исходные детали трасс.
Брент Риттенхаус
3

Используйте метод расширения:

    public static List<string> ColumnList(this IDataReader dataReader)
    {
        var columns = new List<string>();
        for (int i = 0; i < dataReader.FieldCount; i++)
        {
            columns.Add(dataReader.GetName(i));
        }
        return columns;
    }
Роб Седжвик
источник
2

Вы уверены, что можете.


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(" ");
  }

}

Это родом из: http://www.dotnetjunkies.ddj.com/Article/B82A22D1-8437-4C7A-B6AA-C6C9BE9DB8A6.dcik

Иеремия Пешка
источник
1

Этого проще добиться в SQL

var columnsList = dbContext.Database.SqlQuery<string>("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'SCHEMA_OF_YOUE_TABLE' AND TABLE_NAME = 'YOUR_TABLE_NAME'").ToList();
Almett
источник