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

Обратите внимание на способ создания параметра и добавления его в коллекцию параметров команды SELECT. Параметр команды используется в условии отбора (оператор where), его значение выбирается из ComboBox.

Обновление данных связанных таблиц

В настоящий момент вы владеете основными способами работы с объектами DbCommand, DbDataReader и DataView. Поэтому пора вернуться к задаче обновления данных связанных таблиц, осложненной тем, что в паре ключей PK-FK (Primary Key-Foreign Key) первичный ключ является автоинкрементируемым счетчиком.

Проблемы внесения изменений в базу данных, которые мы рассмотрели ранее, обусловлены сложной структурой данных ADO.NET, когда одновременно существует несколько версий строк таблицы и в процессе работы с ними строки переходят из одного состояния в другое. Адаптер связи (и его метод Update) разработан для работы только с одной таблицей и плохо приспособлен для обновления данных с иерархической структурой.

Разрабатывая приложение, решаюшее описанную проблему, мы поступим следующим образом. Создадим класс TablesChange, объект которого будет накапливать и оптимизировать все изменения, которые производятся над строками таблиц БД в прцессе работы пользователя с таблицами Studs и Exams. Манипуляция данными производится с помощью компонентов gridStuds и gridExams класса DataGridView.

·  Скопируйте решение StudentsOleDb и переименуйте копию (решение и проект) в StudentsDBEx.

·  С помощью рефакторинга переименуйте пространство имен (namespace) в StudentsDBEx.

·  Убедитесь, что BindingNavigator имеет обработчик события ItemClicked.

·  Добавьте в проект новый класс TablesChange.

·  Введите следующий код в файл TablesChange.cs

namespace StudentsDBEx

{

public class TablesChange

{

  List<DataRow>[] added, deleted, changed;

  DataTableCollection tables;

  public List<DataRow>[] Added { get { return added; } }

  public List<DataRow>[] Deleted { get { return deleted; } }

  public List<DataRow>[] Changed { get { return changed; } }

  public TablesChange (DataTableCollection col)

  {

   tables = col;

   int count = tables.Count;

   added = new List<DataRow>[count];

   deleted = new List<DataRow>[count];

   changed = new List<DataRow>[count];

   for (int i=0; i<count; i++)

   {

    added[i] = new List<DataRow> ();

    deleted[i] = new List<DataRow>();

    changed[i] = new List<DataRow>();

   }

  }

  internal void RowChanged (object sender, DataRowChangeEventArgs e)

  {

   int id;

   for (id = 0; sender != tables[id]; id++)

    ;

   DataRow row = e.Row;

   List<DataRow>

    newRows = added[id],

    modRows = changed[id],

    delRows = deleted[id];

   switch (e.Action)

   {

   case DataRowAction.Add:

    if (!newRows.Contains (row))

      newRows.Add (e.Row);

    break;

   case DataRowAction.Change:

    if (!newRows.Contains (row) && !modRows.Contains (row))

      modRows.Add (e.Row);

    break;

   case DataRowAction.Delete:

    if (newRows.Contains (row))

      newRows.Remove (row);

    else

    {

      if (modRows.Contains (row))

        modRows.Remove (row);

      if (!delRows.Contains (row))

      {

        DataRow copy = tables[id].NewRow ();

        foreach (DataColumn col in tables[id].Columns)

           copy[col] = row[col];

        delRows.Add (copy);

      }

    }

    break;

   }

  }

  internal void Clear ()

  {

   for (int i = 0; i < tables.Count; i++)

   {

    added[i].Clear ();

    deleted[i].Clear ();

    changed[i].Clear ();

   }

  }

}

}

В состав данных класса входит типизированная коллекция ссылок на таблицы DataTableCollectiontables и три массива объектов, каждый из которых является generic-списком ссылок на те объекты DataRow, которые претерпели изменения. Generic-списки, построенные на основе шаблонов List<DataRow>, оптимально работают с объектами определенного типа. В нашем случае они настроены на хранение объектов DataRow.