Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции OOP c#.doc
Скачиваний:
44
Добавлен:
22.09.2019
Размер:
3.38 Mб
Скачать

4.10. Объекты класса DataRow – строки таблицы

Строки содержат данные таблицы. Отдельная строка представлена объектом класса DataRow. Таблица содержит коллекцию Rows типа DataRowCollection для хранения своих строк. Каждая строка обладает свойством Table, в котором хранится ссылка на таблицу, владеющую строкой.

Для создания строки применяется метод таблицы NewRow(). Метод генерирует пустую строку согласно структуре столбцов таблицы, но не добавляет эту строку в таблицу. Для добавления строки необходимо заполнить ее данными, а затем воспользоваться методом DataTable.Rows.Add(). Существует перегруженный вариант этого метода, который принимает в качестве параметра массив объектов, являющихся значениями полей строки:

// Создали пустую строку с требуемой структурой

DataRow r = Artists.NewRow();

// Заполняем ее содержимым

r["name"] = "Depeche Mode";

// Добавляем в таблицу

Artists.Rows.Add(r);

// Вариант покороче, в котором совмещены сразу три действия

Artists.Rows.Add(new object[2]{null, "Nirvana"});

Рассмотрим вопросы, связанные с редактированием строки таблицы. Прежде всего, требуется получить из таблицы строку для редактирования. В простейшем случае это можно сделать по номеру строки. Если номер строки неизвестен, то можно использовать метод Find() коллекции Rows (подробнее о поиске строк в таблице будет рассказано ниже).

// Знаем номер строки, в данном случае – получаем вторую

DataRow row = Users.Rows[1];

// Можно найти строку по значению первичного ключа

DataRow row_2 = Users.Rows.Find(1);

Любая строка имеет несколько перегруженных индексаторов (свойство Item) для доступа к своим полям. В качестве индекса может использоваться имя столбца (как в предыдущем фрагменте кода), номер столбца или объект DataColumn, представляющий столбец:

// меняем содержимое определенных полей строки

row["user_name"] = "Alex Volosevich";

row["user_address"] = "Mars";

Второй способ редактирования строки аналогичен первому, за исключением того, что добавляются вызовы методов BeginEdit() и EndEdit() класса DataRow:

row.BeginEdit();

row["user_name"] = "Alex Volosevich";

row["user_address"] = "Mars";

row.EndEdit();

Методы BeginEdit() и EndEdit() позволяют буферизировать изменения строки. При вызове EndEdit() коррективы сохраняются в строке. Если вы решите отменить их, вызовите метод CancelEdit(), и строка вернется в состояние на момент вызова BeginEdit().

Есть еще одно отличие между этими двумя способами редактирования строки. Объект DataTable предоставляет события RowChanging, RowChanged, ColumnChanging и ColumnChanged, с помощью которых удается отслеживать изменения строки или поля. Порядок наступления этих событий зависит от того, как редактируется строка – с вызовом методов BeginEdit() и EndEdit() или без них. Если вызван метод BeginEdit(), наступление событий откладывается до вызова EndEdit() (если вызывается CancelEdit() никакие события не наступают).

Третий способ изменения строки – воспользоваться свойством строки ItemArray, Как и свойство Item, ItemArray позволяет просматривать и редактировать содержимое строки. Различие свойств в том, что Item рассчитано на работу с одним полем, а ItemArray принимает и возвращает массив, элементы которого соответствуют полям.

object[] data = new object[] {null, "A. Volosevich", "Mars"};

row.ItemArray = data;

Если необходимо с помощью свойства ItemArray отредактировать содержимое лишь отдельных полей строки, воспользуйтесь ключевым словом null. В предыдущем примере первое поле строки после редактирования осталось без изменений.

Поле строки может содержать пустое значение. Проверить это помогает метод IsNull(). Как и индексатор Item, IsNull() принимает в качестве параметра имя поля, его порядковый номер или объект DataColumn. Чтобы сделать значение поля пустым, требуется использовать специальный класс DBNull:

if (!row.IsNull("user_address"))

row["user_address"] = DBNull.Value;

Чтобы удалить строку, достаточно вызвать метод Delete() объекта DataRow. После вызова метода Delete() строка помечается как удаленная, из базы она будет удалена после «закачки» содержимого DataSet в базу. Можно удалить строку из коллекции Rows таблицы, воспользовавшись методами коллекции Remove() или RemoveAt(). Первый метод принимает как параметр объект DataRow, второй – порядковый номер строки. Если строка удалена подобным образом, то при синхронизации изменений с БД, строка из базы удалена не будет.

Пусть имеется некий рассоединенный набор данных, в который помещена информация из БД. Допустим, что информация была изменена (в таблице редактировались, добавлялись или удалялись строки), и необходимо переместить содержимое набора обратно в базу. Было бы не продуктивно «перекачивать» в базу набор целиком. Более эффективный вариант – отслеживание изменений, и внесение в базу только корректирующих поправок.

Для поддержки корректирующих изменений базы каждая строка имеет состояние и версию. Состояние строки хранится в свойстве RowState и принимает следующие значения из перечисления DataRowState:

  • Unchanged – строка не менялась (совпадает со строкой в базе);

  • Detached – строка не относится к объекту DataTable;

  • Added – строка добавлена в объект DataTable, но не существует в БД;

  • Modified – строка была изменена по сравнению со строкой из базы;

  • Deleted – строка ожидает удаления из базы.

В таблице 27 показано, как может изменяться состояние отдельной строки.

Таблица 27

Изменение состояния строки DataRow

Действие

Код

Значение

RowState

Создание строки, не относящейся к объекту DataTable

row = tbl.NewRow();

row["id"] = 100;

Detached

Добавление новой строки в DataTable

tbl.Rows.Add(row);

Added

Получение существующей строки

row = tbl.Rows[0];

Unchanged

Редактирование строки

row.BeginEdit();

row["id"] = 10000;

row.EndEdit();

Modified

Удаление строки

row.Delete();

Deleted

С помощью свойства RowState можно найти в таблице измененные строки. Кроме этого, для любой строки существует возможность просмотреть, каким было значение ее полей до изменения. Свойство строки Item имеет перегруженный вариант, принимающий значение из перечисления DataRowVersion:

  • Current – текущее значение поля;

  • Original – оригинальное значение поля;

  • Proposed – предполагаемое значение поля (действительно только при редактировании записи с использованием BeginEdit()).

Следующий фрагмент кода изменяет содержимое поля name первой строки таблицы Artists, а затем выводит оригинальное и текущее содержимое поля:

// Заполняем таблицу из базы, чтобы была "оригинальная" версия

string con = "Server=. . .";

SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM Artists", con);

DataTable Artists = new DataTable("Artists");

da.Fill(Artists);

// Меняем содержимое первой строки

DataRow row = Artists.Rows[0];

row["name"] = "Alex";

Console.WriteLine(row["name", DataRowVersion.Current]);

Console.WriteLine(row["name", DataRowVersion.Original]);

При редактировании строки с использованием методов BeginEdit() и EndEdit() доступна версия с «предполагаемым» содержимым строки. После вызова EndEdit() все изменения сохраняются в текущей версии строки. Тем не менее, до этого изменения считаются отложенными, поскольку их удается отменить вызовом метода CancelEdit(). Чтобы при редактировании строки просмотреть предполагаемое значение поля, воспользуйтесь свойством Item со вторым элементом DataRowVersion.Proposed:

row.BeginEdit();

row["name"] = "Alex";

// Выводит состояние записи до вызова BeginEdit()

Console.WriteLine(row["name", DataRowVersion.Current]);

// Естественно, выводит то значение, которое прочитали из базы

Console.WriteLine(row["name", DataRowVersion.Original]);

// Выводит Alex – предполагаемое значение

Console.WriteLine(row["name", DataRowVersion.Proposed]);

row.EndEdit();

// Выводит Alex – мы "закрепили" значение, вызвав EndEdit()

Console.WriteLine(row["name", DataRowVersion.Current]);

Таблица 28 показывает возможные значения, возвращаемые свойством Item в зависимости от указанной версии ([Искл.] обозначает генерацию исключительной ситуации при попытке получить определенную версию).

Таблица 28

Значения свойства Item в зависимости от версии строки

Пример

Current

Original

Proposed

Только что созданная строка, не связанная с таблицей

row = tbl.NewRow();

row["id"] = 10;

[Искл.]

[Искл.]

10

В таблицу добавлена новая строка

tbl.Rows.Add(row);

10

[Искл.]

[Искл.]

Данные загружены из базы, из таблицы получена существующая строка

row = tbl.Rows[0];

11

1

[Искл.]

Первое изменение существующего поля

row.BeginEdit();

row["id"] = 100;

1

1

100

После первого изменения

row.EndEdit();

100

1

[Искл.]

После второго изменения содержимого поля

row.BeginEdit();

row["id"] = 300;

row.EndEdit();

300

1

[Искл.]

После отмены изменений

row.BeginEdit();

row["id"] = 500;

row.CancelEdit()

300

1

[Искл.]

После удаления записи

DataRow row = Artists.Rows[0];

row.Delete();

[Искл.]

1

[Искл.]

Для контроля существования версии можно использовать метод HasVersion():

if(row.HasVersion(DataRowVersion.Current))

Console.WriteLine("Current version exists");

Любая строка предоставляет методы AcceptChanges() и RejectChanges(). Вызов первого метода закрепляет в строке все отложенные изменения, а вызов второго – отбрасывает отложенные изменения. Иными словами, вызов AcceptChanges() приводит к замене значений Original-версии строки значениями из Current-версии и установке у строки RowState = Unchanged. Вызов RejectChanges() устанавливает RowState= Unchanged, но значения Current-версии строки меняются на значения Original-версии. При загрузке изменений DataSet в базу у каждой строки неявно вызывается метод AcceptChanges(). Внимание: явное использование указанных методов может породить проблемы при синхронизации набора данных и базы.

Отдельная строка таблицы, а также отдельное поле строки позволяют задать текстовую метку при наличии ошибочных значений. Метка для поля устанавливается методом SetColumnError(), а читается методом GetColumnError(). Можно пометить всю строку, используя свойство строки RowError. Если метка была указана, то свойство строки HasErrors = true. Поля с метками ошибок могут быть получены при помощи вызова метода строки GetColumnsInErrors(). Метод ClearErrors() удаляет все метки ошибок в полях строки и очищает свойство RowError. Работу с описанными методами демонстрирует следующий пример:

// Получили строку таблицы

DataRow row = Artists.Rows[0];

// После продолжительного анализа решили, что с полем name

// что-то не так; поставим метку на это поле

// (можно было использовать номер поля)

row.SetColumnError("name", "Wrong Name");

// Заодно пометим всю строку как "бракованную"

row.RowError = "Row with Errors";

// Где-то в коде: проверим-ка мы строку на предмет ошибок...

if (row.HasErrors) {

// Выведем описание ошибки для всей строки

Console.WriteLine(row.RowError);

// Получим массив колонок (полей) с ошибками

DataColumn[] err_Columns = row.GetColumnsInError();

// Пройдемся по массиву и выведем информацию об ошибках

foreach (DataColumn dc in err_Columns) {

Console.WriteLine(dc.ColumnName);

Console.WriteLine(row.GetColumnError(dc));

}

// Считаем, что с ошибками разобрались; очистим сообщения

row.ClearErrors();

}