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

В .Net Framework 1.1 не было класса BindingSource и там мы работали с объектами класса CurrencyManager. Теперь (в .Net 2.0 и выше) объект CurrencyManager живет внутри каждого объекта BindingSource. Мы можем выудить его оттуда, например так:

CurrencyManager cmExam = bsExams.CurrencyManager;

В нашем сценарии нет необходимости использовать cmExam, хотя вы можете воспользоваться им для доступа к текущей записи (строке связанной таблицы). Для этого надо обратиться к свойству Current объекта cmExam. Например.

DataRow row = (cmExam.Current as DataRowView).Row;

row["Course"] = "Physics";

Здесь мы изменяем значение поля Course текущей строки таблицы Exams. Какая строка будет текущей? Это зависит от момента времени, в который мы добываем ссылку на CurrencyManager. Если это происходит внутри реакции на событие PositionChanged объекта, bsExams, то текущий элемент определяется позициями активных ячеек как в первом, так и во втором DataGridView. Синхронизатор cmExam умеет отслеживать перемещения по обоим DataGridView благодаря настройкам свойств поглотившего его BindingSource.

Свойство Current объекта cmExam дает доступ к объекту DataRowView, который, в свою очередь, имеет свойство Row типа DataRow. Этот объект и является ссылкой на текущую строку второй, связанной таблицы (ds.Tables[1]). Заметим, что ссылку на CurrencyManager можно добыть и другим способом — из контекста связей DataGridView. Контекст связей добывается таким образом:

CurrencyManager cmExam = gridExam.BindingContext[ds, "Studs.StudsExams"] as CurrencyManager;

Обратите внимание на хитрый индексатор вида [objectdataSource, stringdataMember] класса BindingContext. Любопытно, что следующий, более простой оператор дает тот же самый результат.

CurrencyManager cmExam = BindingContext[ds, "Studs.StudsExams"] as CurrencyManager;

Здесь мы обращаемся к контексту связей формы, а не gridExam. Такой контекст содержит синхронизаторы всех элементов формы. Контекст gridExam.BindingContext, в свою очередь, содержит синхронизаторы всех элементов, вложенных в gridExam. Но так как пара индексов [ds, "Studs.StudsExams"] единственным образом определяет синхронизатор cmExam, то результат один и тот же. Некоторые детали механизма DataBinding описаны также в документе ADO Intro 1.doc.

Продолжая развитие приложения, удалите код, с помощью которого мы исследовали особенности привязки других элементов к данным синхронизатора bsExams. Оставьте лишь код, привязывающий два элемента DataGridView к данным синхронизаторов bsStuds и bsExams. Если хотите сохранить код, то создайте копию всего проекта.

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

При обновлении данных двух связанных таблиц (методом Update класса DbDataAdapter) алгоритм управления должен быть более сложным, так как важен порядок обновления таблиц. Раздел MSDN Hierarchical Update освещает эту тему достаточно подробно. Проблемы возникают потому, что адаптеры связи в принципе могут вносить изменения (за одно обращение к методу Update) только в одну таблицу. Проведите следующий опыт.

·  Запустите приложение, добавьте нового студента и, не нажимая кнопки Save, добавьте в таблицу экзаменов (для нового студента) пару экзаменов и нажмите кнопку Save.

·  Удалите вновь добавленного судента и нажмите кнопку Save.

·  При попытке сохранить данные мы получаем исключение DBConcurrencyException.

Любой другой, более запутанный алгоритм изменения данных DataSet при попытке их сохранения в базе данных также приводит к исключению.

Есть и еще одна проблема, связанная с автоинкрементируемыми ключами. При добавлении новых строк в обе таблицы ADO.NET пытается генерировать новые значения индексов для первичных ключей StudID и ExamID и вы видите работу этого механизма потому, что установлено свойство MissingSchemaAction для обоих адаптеров связи. Если сгенерированные индексы не совпадут с теми, что определены СУБД, то мы получим исключение.