- •А.А. Волосевич
- •4. БАзы данных и технология ado.Net 3
- •4. БАзы данных и технология ado.Net
- •4.1. Архитектура ado.Net
- •4.2. Соединение с базой данных
- •4.3. Выполнение команд и запросов к базе данных
- •4.4. Параметризированные запросы
- •4.5. Чтение данных и объект DataReader
- •4.6. Рассоединенный набор данных
- •4.7. Объект класса DataColumn – колонка таблицы
- •4.8. Объекты класса DataRow – строки таблицы
- •4.9. Работа с объектом класса DataTable
- •4.10. Схема данных и типизированные dataset
- •4.11. Навигация, Поиск и фильтрация данных в DataSet
- •4.12. Класс DataView
- •4.13. Заполнение Рассоединенного набора данных
- •4.14. СиНхронизация набора данных и базы
- •4.15. Работа с транзакциями
4.8. Объекты класса DataRow – строки таблицы
Строки содержат данные таблицы. Отдельная строка представлена объектом класса DataRow. Таблица содержит коллекцию Rows для хранения своих строк. Каждая строка обладает свойством Table, в котором хранится ссылка на таблицу, владеющую строкой.
Для создания строки применяется метод таблицы NewRow(). Метод генерирует пустую строку согласно структуре таблицы, но не добавляет эту строку в таблицу. Для добавления строки необходимо заполнить ее данными, а затем воспользоваться методом DataTable.Rows.Add(). Существует перегруженный вариант этого метода, который принимает в качестве параметра массив объектов, являющихся значениями полей строки:
var Artists = new DataTable("Artists");
// после создания таблицы добавили в неё объекты-столбцы
// создали пустую строку с требуемой структурой
var r = Artists.NewRow();
// заполняем её содержимым
r["name"] = "Metallica";
// добавляем в таблицу
Artists.Rows.Add(r);
// вариант покороче, в котором совмещены сразу три действия
Artists.Rows.Add(new object[] { null, " Metallica" });
Рассмотрим вопросы, связанные с редактированием строки. Прежде всего, требуется получить из таблицы строку для редактирования. В простейшем случае это можно сделать по номеру строки1.
// знаем номер строки, в данном случае – получаем вторую
DataRow row = Users.Rows[1];
Любая строка имеет несколько перегруженных индексаторов для доступа к своим полям. В качестве индекса может использоваться имя столбца, номер столбца или объект DataColumn, представляющий столбец.
// меняем содержимое определенных полей строки
row["user_name"] = "the user ";
row["user_address"] = "the address";
Платформа .NET версии 3.5 содержит класс DataRowExtensions с двумя универсальными методами расширения для класса DataRow: Field<T>() и SetField<T>(). Эти методы позволяют осуществить типизированный доступ к полям строки. Как и индексаторы DataRow, методы имеют различные перегруженные версии.
int id = row.Field<int>(0);
row.SetField<int>("id", 25);
Возможен способ редактирования строки с буферизированием изменений. Перед началом редактирования нужно вызвать у строки метод BeginEdit(). При вызове EndEdit() в конце редактирования коррективы сохраняются в строке. Если нужно отменить их, то следует вызвать метод CancelEdit(), и строка вернется в состояние на момент вызова BeginEdit().
row.BeginEdit();
row["user_name"] = "the user";
row["user_address"] = "the address";
row.EndEdit();
Есть ещё одно отличие между этими двумя способами редактирования. Объект DataTable предоставляет события RowChanging, RowChanged, ColumnChanging и ColumnChanged, с помощью которых удаётся отслеживать изменения строки или поля. Порядок наступления этих событий зависит от того, как редактируется строка – с вызовом методов BeginEdit() и EndEdit() или без них. Если вызван метод BeginEdit(), наступление событий откладывается до вызова EndEdit() (если вызвать CancelEdit() никакие события не наступают).
Третий способ изменения строки – воспользоваться свойством строки ItemArray, Свойство ItemArray позволяет просматривать и редактировать содержимое строки, но его тип – это массив, элементы которого соответствуют полям строки. Если необходимо отредактировать содержимое лишь некоторых полей, воспользуйтесь литералом null.
// меняем содержимое второго и третьего полей
row.ItemArray = new object[] { null, "the user", "the address" };
Чтобы удалить строку, достаточно вызвать метод Delete() объекта DataRow. После вызова метода Delete() строка помечается как удаленная, из базы она будет удалена после «закачки» содержимого DataSet в базу. Можно удалить строку из коллекции Rows таблицы, воспользовавшись методами коллекции Remove() или RemoveAt(). Если строка удалена подобным образом, то при синхронизации изменений с БД, из базы строка удалена не будет.
Пусть имеется некий рассоединенный набор данных, в который помещена информация из БД. Допустим, что информация была изменена (в таблице редактировались, добавлялись или удалялись строки), и необходимо переместить содержимое набора обратно в базу. Было бы не продуктивно «перекачивать» в базу набор целиком. Более эффективный вариант – отслеживание изменений, и внесение в базу только корректирующих поправок.
Для поддержки корректирующих изменений базы каждая строка имеет состояние и версию. Состояние строки хранится в свойстве RowState и принимает следующие значения из перечисления DataRowState:
-
Unchanged – строка не менялась (совпадает со строкой в базе);
-
Detached – строка не относится к объекту DataTable;
-
Added – строка добавлена в объект DataTable, но не существует в БД;
-
Modified – строка была изменена по сравнению со строкой из базы;
-
Deleted – строка ожидает удаления из базы.
В табл. 7 показано, как может изменяться состояние отдельной строки.
Таблица 7
Изменение состояния строки 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 можно найти в таблице изменённые строки. Кроме этого, для любой строки существует возможность просмотреть, каким было значение ее полей до изменения. Индексатор строки и методы расширения Field<T>() и SetField<T>() имеют перегруженные версии, принимающий значение из перечисления DataRowVersion:
-
Current – текущее значение поля;
-
Original – оригинальное значение поля;
-
Proposed – предполагаемое значение поля (действительно только при редактировании записи с использованием BeginEdit()).
Следующий фрагмент кода изменяет содержимое поля name первой строки таблицы Songs, а затем выводит оригинальное и текущее содержимое поля:
var Songs = new DataTable("Songs");
// заполняем таблицу из базы, чтобы была "оригинальная" версия
var da = new SqlDataAdapter("SELECT * FROM Songs", "Server=. . .");
da.Fill(Songs);
// меняем содержимое первой строки
var row = Songs.Rows[0];
row["name"] = "NAME";
Console.WriteLine(row["name", DataRowVersion.Current]);
Console.WriteLine(row["name", DataRowVersion.Original]);
Табл. 8 показывает возможные значения, возвращаемые индексатором в зависимости от указанной версии ([Искл.] обозначает генерацию исключительной ситуации при попытке получить определенную версию).
Таблица 8
Значения свойства 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(). Внимание: явное использование указанных методов может породить проблемы при синхронизации набора данных и базы.
С помощью методов SetAdded() и SetModified() свойство RowState строки может быть изменено программно. Использование этих методов приводит к принудительному обновлению данных при передаче изменений в базу, даже если сами данные не претерпели изменений.