SqlDataReader HasRows=True, но нет данных

Я подключаюсь к базе данных SQL Server 2012, чтобы запросить одно значение на основе идентификатора. (Возможно, стоит упомянуть, что эта база данных находится на сервере на другом континенте от моей машины для разработки, поэтому задержки довольно велики. Где-то около 100 мс).

Похоже, что запрос выполняется успешно. Свойство HasRows объекта SqlDataReader имеет значение true, поэтому я пытаюсь использовать это значение для присвоения переменной. Когда я запускаю программу в обычном режиме, я сталкиваюсь с исключением с сообщением 'Given key was not present in the dictionary'. Если я остановлю выполнение и проверю объект SqlDataReader и перечислю результаты. Сначала мне говорят 'enumeration yielded no results', а затем, когда я продолжаю выполнение, я получаю другое исключение с сообщением 'invalid attempt to read when no data is present'

Вот рассматриваемый код:

SqlConnection sql_conn = new SqlConnection(ConnectionString);
SqlCommand sql_cmd = new SqlCommand(String.Format("select ItemType from ItemTable where ItemID='{0}'", item_id), sql_conn);

Console.WriteLine(sql_cmd.CommandText);

sql_conn.Open();

SqlDataReader rdr = sql_cmd.ExecuteReader();

rdr.Read();

if (rdr.HasRows) //True
{
    item_type= TypesMap[rdr["ItemType"].ToString()]; //Either 'given key not found in dictionary' or 'invalid attempt to read when no data is present'
}

Я выполнил инструкцию SQL в SQL Server Management Studio, и она прошла успешно. Я попытался жестко закодировать ItemID в инструкции в коде C#, и существуют те же ошибки.

Что еще я могу сделать, чтобы отладить это? Кажется, все в порядке, пока я не попытаюсь получить доступ к результатам запроса.


person Luke    schedule 15.04.2016    source источник
comment
Не вызывая этой проблемы, но: я бы изменил порядок, сначала использовал HasRows, чтобы проверить, есть ли какие-либо строки, а затем использовал Read, чтобы перейти к следующей записи.   -  person Tim Schmelter    schedule 15.04.2016
comment
Два сообщения об ошибках вызваны двумя совершенно разными ситуациями. Что вы получаете с этим кодом?   -  person Steve    schedule 15.04.2016
comment
Я бы пропустил reader.HasRows и реализовал бы такую ​​логику, как if (reader.Read()) { item_type= TypesMap[rdr["ItemType"].ToString()]; }   -  person Nino    schedule 15.04.2016
comment
Если вы хотите получить только одно значение, используйте rdr.ExecuteScalar().   -  person Dmitri Trofimov    schedule 15.04.2016


Ответы (3)


Вы должны отладить: кажется, что TypesMap нет ключа, прочитанного из базы данных:

// Wrap IDisposable into using
using (SqlConnection sql_conn = new SqlConnection(ConnectionString)) {
  // Make SQL readable
  // Make SQL parametrized (and not formatted) when it's possible
  String sql = 
    @"select ItemType 
        from ItemTable 
       where ItemID = @prm_ItemId"; 

  // Wrap IDisposable into using
  using (SqlCommand sql_cmd = new SqlCommand(sql, sql_conn)) {
    // I don't know ItemID's type that's why I've put AddWithValue 
    sql_cmd.Parameters.AddWithValue("@prm_ItemId", item_id);

    // Wrap IDisposable into using
    using (SqlDataReader rdr = sql_cmd.ExecuteReader()) {
      // rdr.HasRows is redundant - rdr.Read() returns true if record has been read
      if (rdr.Read()) {
        String key = Convert.ToString(rdr.GetValue(0));
        // Put break point here: what is the "key" value?
        item_type = TypesMap[key];
      }
    }
  } 
}

Редактировать: как Люк упомянул в комментарии, причиной ошибки было то, что сравнение ключей должно быть нечувствительным к регистру, поэтому поправка должна объяснить .Net, как сравнить ключи:

var TypesMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

...
TypesMap.Add("aBc", "xyz"); 
String test = TypesMap["Abc"]; // return "xyz"; notice "aBc" and "Abc"
person Dmitry Bychenko    schedule 15.04.2016
comment
Спасибо. В конце концов, это не было связано с запросом к базе данных. Как всегда, это была просто глупая ошибка, мне нужно было сделать ключ словаря строчным. - person Luke; 15.04.2016
comment
@Luke: лучше не делать ключи строчными, а назначить соответствующий компаратор: TypesMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); - person Dmitry Bychenko; 15.04.2016
comment
Я не знал об этом. Спасибо! - person Luke; 15.04.2016

Как указал Дмитрий, «данный ключ не найден ...» - это не вещь БД, а вещь словаря. Ниже я добавил простую проверку, чтобы убедиться, что ключ находится в словаре — если это так, мы можем назначить его для item_type.

Кроме того, если HasRows() не делает того, что вы ожидаете, попробуйте следующее. Это стандартный способ чтения из БД:

using (SqlDataReader results = sql_cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
    while (results.Read())
    {
        string Key = rdr["ItemType"].ToString();
        if (TypesMap.ContainsKey(Key))
            item_type = TypesMap[Key];       
    }
}
person Jag    schedule 15.04.2016
comment
ваша стандартная практика НЕ ​​является стандартной практикой. вы всегда должны вызывать hasrows как стандартную практику. - person Max Alexander Hanna; 14.02.2019
comment
@MaxAlexanderHanna Хм, я не говорил и не подразумевал, что это стандартная практика. Именно так, как я это делаю. Отсюда слово I и отсутствие упоминания о стандартной практике. - person Jag; 15.02.2019
comment
если в нем нет строк, есть большая проблема, и ваша попытка стандартизировать практику на самом деле контрпродуктивна. понятия не имею, как вы получили два балла за этот ответ. - person Max Alexander Hanna; 18.02.2019
comment
@MaxAlexanderHanna, лол, правда. Вы поднимаете ответ трехлетней давности (это даже не принятый ответ) и обвиняете меня в том, что я контрпродуктивен? - person Jag; 20.02.2019

Я преобразовал:

dto.Id = (int)record["Id"];  

To:

dto.Id = (int)record[0]; 

Это сработало для меня.

person Masoud Sadeghi    schedule 16.07.2017