2016-11-08 3 views
1

Прежде чем вы отметите этот вопрос как дубликат, вот сложная часть, которую я не понимаю. Эта ошибка является спорадической, я считаю, что код правильный, и он всегда работает, и я обрабатываю возможные ошибки с условием if else внутри части Reader. Вот код:Код не синхронизированный метательный индекс был вне границ массива?

public static Tuple<int, string> GetIDAndString(string term) 
{ 
    try 
    { 
     using (SqlConnection con = GetConnection()) 
     using (cmd = new SqlCommand()) 
     using (myReader) 
     { 
      int ID = 0; 
      string status = string.Empty; 
      cmd.Connection = con; 
      con.Open(); 
      cmd.CommandText = @"SELECT t.TableID, t.Status 
           FROM Table t WITH (NOLOCK) /* I know NOLOCK is not causing the mistake as far as I know */ 
           WHERE t.Term = @term"; 
      cmd.Parameters.AddWithValue("@term", term); 

      myReader = cmd.ExecuteReader(); 
      while(myReader.Read()) 
      { 
       ID = myReader.IsDBNull(0) ? 0 : myReader.GetInt32(0); 
       status = myReader.IsDBNull(1) ? string.Empty : myReader.GetString(1).Trim(); 
      } 

      myReader.Close(); 

      return new Tuple<int, string>(ID, status); 
     } 
    } 
    catch (Exception) 
    { 
     throw; 
    } 
} 

Я знаю, что я должен использовать класс вместо кортежей, но я не могу изменить, что существующий код и как вы можете видеть. Поэтому основная проблема заключается в том, что на рабочем сервере в этом методе был Index out of bounds array exception, но я не могу определить, в чем проблема.

Даже если этот термин не найден в запросе, myReader не войдет, и я верну ID = 0, status = string.Empty. Иногда, когда я отлаживаю код и работаю над develpment server, мой код начинает крушить всюду, показывая мне исключения, где протестирован код, и я должен снова открыть решение, чтобы избежать этого (я не нашел решения для этого, даже не очистка раствора).

Поэтому я надеюсь, что у кого-то есть опыт с чем-то подобным в production server. У меня нет спецификаций для производственного сервера, поэтому я ничего не знаю о сервере.

+0

Создать новый считыватель данных, не разделяя его! Это может вызвать проблемы! – mybirthname

+0

Согласитесь с предыдущим комментарием, либо создайте новый экземпляр, либо синхронизируйте с ним доступ. –

+0

@mybirthname, я новичок в программировании, вы сказали, что не разделяете dataReader. Вы имеете в виду не использовать класс, в котором я объявлял защищенный статический SqlDataReader, а затем использовал его в блоке использования? –

ответ

1

Сначала вам не нужен блок try/catch, вы ничего с ним не делаете. После этого не разделяйте SqlDataReader в классе, это может вызвать проблемы и, вероятно, проблема возникает из-за этого. Вы переписываете значение ID и Status все время в свое время. Вероятно, хорошей идеей будет позвонить Top 1 по вашему запросу и заказать его с помощью правильного поля. Также нет необходимости в Dispose()SqlCommand, Конструктор SqlCommand звонит SupressFinalization().

Почему эта проблема может случиться: представьте, что ваш запрос возвращает 1000 записей с помощью столбцов TableID и Status, и вы вводите цикл while. В этот момент другой пользователь входит в ваше приложение и выполняет другой метод, который перезаписывает SqlDataReader и возвращает 5 записей только с одним столбцом. На следующей итерации цикла while вы получите свое исключение. Из-за этого вы никогда не должны определять своих читателей как static для всего класса. Статические переменные распределяются между всеми пользователями приложения.

public static Tuple<int, string> GetIDAndString(string term) 
{ 
    int ID = 0; 
    string status = string.Empty; 

    using (SqlConnection con = GetConnection()) 
    { 
     SqlCommand cmd = new SqlCommand(); 
     cmd.Connection = con; 
     con.Open(); 

     cmd.CommandText = @"SELECT t.TableID, t.Status 
          FROM Table t WITH (NOLOCK) /* I know NOLOCK is not causing the mistake as far as I know */ 
          WHERE t.Term = @term"; 
     cmd.Parameters.AddWithValue("@term", term); 


     using(SqlDataReader myReader = cmd.ExecuteReader()) 
     { 
      while(myReader.Read()) 
      { 
       ID = myReader.IsDBNull(0) ? 0 : myReader.GetInt32(0); 
       status = myReader.IsDBNull(1) ? string.Empty : myReader.GetString(1).Trim(); 
      } 

     } 

    } 

    return new Tuple<int, string>(ID, status); 
} 
+0

Отличное объяснение! Я дам вам правильный ответ, потому что он такой же, как у другого, но с примером моего кода с лучшими практиками. Остается только надеяться, что вы это проясните мне. Если я использую 'protected static SqlTransaction' в методе' GetConnection() ', должен ли я вносить изменения, подобные SqlReader внутри класса? Или просто для SqlReader? Спасибо –

+0

@JorgeF да не используют статическую транзакцию. проверьте статью msdn: https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqltransaction(v=vs.110).aspx. – mybirthname

+0

Спасибо, я сделаю эти изменения. –

0

это, вероятно, происходит, когда вы делаете ID = myReader.IsDBNull(0) ? 0 : myReader.GetInt32(0); или status = myReader.IsDBNull(1) ? string.Empty : myReader.GetString(1).Trim();, потому что результирующий набор не соответствует вашим ожиданиям. Вы должны добавить регистрацию строки читателя до ее фактического чтения, может помочь вам определить проблему.

0

Я думаю, проблема связана с полем myReader, которое, я полагаю, является статическим. Если вы посмотрите на документацию (я полагаю, это тип поля) в https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader(v=vs.110).aspx, вы обнаружите, что методы экземпляра не являются потокобезопасными, поэтому вы должны синхронизировать доступ к этому полю.

0

using (myReader) отражает значение, которое читатель имел в то время и распоряжается этим позже. Он не помнит переменную. Это должно быть так, как вы можете видеть из этого примера: using (Random() ? myReader : null). Очевидно, что язык C# не будет повторно выполнять это выражение во время удаления. Он запускает его только один раз.

Итак, вы избавляетесь от старого/другого читателя.

В случае, если вы делите объекты между потоками (возможно, используя статические переменные), это тривиально - условие гонки. Не делай этого. Используйте местные жители. Здесь нет необходимости/преимущества использования статических переменных.

Смежные вопросы