ADO.NET. Управление базами данных. Связь по схеме OLE DB провайдера. Коррекция стилей DataGridView, страница 22

   while (reader.Read())

    Console.WriteLine(reader["Name"] + ", " + reader["Addr"]);

   reader.Close();

   cn.Close();

  }

  void bn_ItemClicked(object sender, ToolStripItemClickedEventArgs e)

  {

   switch (e.ClickedItem.Text)

   {

    case "&Save": UpdateDB(); break;

   }

  }

  void UpdateDB() {}   // Этот метод пока бездействует

}

Ваше внимание должно быть обращено на код метода ConnectAndRead. Проведите его анализ и запустите приложение в режиме отладки (F5). В окне Output вы должны видеть содержимое двух столбцов таблицы Studs.

Для чтения данных таблицы студентов мы пользуемся методом ExecuteReader, который обязательно имеется во всех классах DbCommand. Вcпомните, что все классы этого типа (SqlCommand, OleDbCommand, OdbcCommand и OracleCommand) обязаны реализовать интерфейс IDbCommand, а значит и все методы этого интерфейса. Обратите внимание на следующие моменты:

·  Мы не пользуется услугами классов DataSet, OleDbDataAdapter и DataGridView.

·  Интерфейсы IDataReader и IDbCommand реализованы классами OleDbDataReader и OleDbCommand.

·  С помощью методов CreateCommand и ExecuteReader мы добываем ссылки на реализации интерфейсов внутри классов.

·  Если изменится тип источника данных (например, вместо OLE DB он станет SQL Server), то фрагмент кода, который производит чтение, менять не придется.

·  Данные выводятся в окно Output только при запуске в режиме отладки (F5).

Замените интерфейсы на классы, вновь запустите приложение и убедитесь, что в его поведении ничего не изменилось, за исключением того, что теперь потеряна общность в соединении с провайдерами.

Результатом выражения reader["StudID"], или reader[0] является индекс студента (поле первичного ключа), а reader["Addr"] — его адрес. Так как индексаторы возвращают обобщенный объект, то необходимо приводить типы, например: int id = (int) reader[0];. Но это можно сделать и по-другому, так как DbDataReader имеет ряд методов вида Get*(int), например: GetString, GetInt32. Предыдущий фрагмент можно записать так: int id = reader.GetInt32(0). Если поле не задано, то описанный подход выдаст исключение, поэтому надо защищать код с помощью метода IsDBNull(int), например:

if (!reader.IsDBNull(3))

   name = reader["Addr"];

·  Восстановите интефейсы и в качестве упражнения добейтесь того, чтобы выборка данных (столбцы Name и Addr) отображались в DataGridView, а не в окне Output.

·  Сделайте это без помощи адаптера и DataSet. Ниже приведена одна из возможных версий решения этой задачи. Вставьте этот код в метод ConnectAndRead.

cn.Open();

IDbCommand cmd = cn.CreateCommand();

cmd.CommandText = "SELECT Name, Addr FROM Studs";

IDataReader reader = cmd.ExecuteReader();

DataTable dt = new DataTable();

dt.Columns.Add("Name"); dt.Columns.Add("Addr");

while (reader.Read())

{

  DataRow row = dt.NewRow();     // Внимание !!

  row["Name"] = reader["Name"];  // row[0] = reader[1];

  row["Addr"] = reader["Addr"];   // row[1] = reader[3];

  dt.Rows.Add(row);

}

reader.Close();

cn.Close();

BindingSource bsStud = new BindingSource(dt, "");

bn.BindingSource = bsStud;

gridStud.DataSource = bsStud;

Временная таблица DataTable заполняется с помощью DbDataReader и к ее данным осуществляется привязка объекта gridStud. При обращении к строкам DataRow и DbDataReader можно пользоваться индексаторами двух типов. Значения числовых индексов определяются схемами таблиц. Так как мы запросили только две колонки, то временная таблица dt имеет всего две колонки. Объект DbDataReader, в отличие от нее, имеет полный набор колонок, который соответствует схеме таблицы студентов. Поэтому числовые индексы (см. код row[1] = reader[3];) не совпадают. При задании этих индексов надо держать в голове схемы обеих таблиц.

Операции со множеством строк