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

4.16. СиНхронизация набора данных и базы

Пусть в набор данных заносится информация из БД при помощи адаптера:

SqlDataAdapter da = new SqlDataAdapter(. . .);

DataSet CD_Rent = new DataSet("CD_Rent");

da.Fill(CD_Rent, "Artists");

Для переноса изменений из набора в базу используется метод адаптера Update(). Данный метод обновляет в базе одну таблицу набора (всегда!), которая, как правило, задается через параметр метода. Однако попытка выполнения следующего кода вызовет исключительную ситуацию:

CD_Rent.Tables["Artists"].Rows[3]["name"] = "Alex";

da.Update(CD_Rent, "Artists");

System.InvalidOperationException: Update requires a valid

UpdateCommand when passed DataRow collection with modified rows.

Дело в том, что при создании адаптера формируется только SelectCommand – команда для выборки данных. Остальные свойства-команды адаптера не инициализированы.

Программист может настроить необходимые команды вручную. Вначале рассмотрим SQL-синтаксис возможных команд для обновления информации в нашем примере:

INSERT INTO Artists(id ,name) VALUES (@p1, @p2)

DELETE FROM Artists WHERE (id = @p1) AND (name = @p2)

UPDATE Artists SET id = @p1, name= @p2

WHERE (id= @p3) AND (name= @p4)

В принципе, этот текст можно скопировать в свойство CommandText команд, которые будут созданы. Отдельного пояснения требует настройка параметров. Параметр, кроме установки таких свойств как имя и тип, должен быть связан со столбцом таблицы из набора данных, а в случае с командой UPDATE – еще и с определенной версией информации в столбце. Для этого используются свойства параметра SourceColumn и SourceVersion. Приведем полный текст создания и настройки команд:

// Создали соединение, которое будут использовать наши команды

SqlConnection con = new SqlConnection(. . .);

// Создали три объекта-команды

SqlCommand ins_cmd = con.CreateCommand();

SqlCommand del_cmd = con.CreateCommand();

SqlCommand upd_cmd = con.CreateCommand();

// Настраиваем текст команд

ins_cmd.CommandText = "INSERT INTO Artists(id,name) VALUES (@p1,@p2)";

del_cmd.CommandText = "DELETE FROM Artists" +

"WHERE (id=@p1) AND (name=@p2)";

upd_cmd.CommandText = "UPDATE Artists SET id=@p1, name= @p2" +

"WHERE (id= @p3) AND (name= @p4)";

// Займемся параметрами

// Создадим два параметра и поместим их в коллекцию

ins_cmd.Parameters.Add("@p1", DbType.Int32);

ins_cmd.Parameters.Add("@p2", DbType.String);

// Дополнительная настройка – укажем столбец, из которого

// берется значение параметра

ins_cmd.Parameters[0].SourceColumn = "id";

ins_cmd.Parameters[1].SourceColumn = "name";

// В случае с командой удаления – аналогичные действия

del_cmd.Parameters.Add("@p1", DbType.Int32);

del_cmd.Parameters.Add("@p2", DbType.String);

del_cmd.Parameters[0].SourceColumn = "id";

del_cmd.Parameters[1].SourceColumn = "name";

// Для команды обновления число параметров в два раза больше

upd_cmd.Parameters.Add("@p1", DbType.Int32);

upd_cmd.Parameters.Add("@p2", DbType.String);

upd_cmd.Parameters.Add("@p3", DbType.Int32);

upd_cmd.Parameters.Add("@p4", DbType.String);

upd_cmd.Parameters[0].SourceColumn = "id";

upd_cmd.Parameters[1].SourceColumn = "name";

upd_cmd.Parameters[2].SourceColumn = "id";

upd_cmd.Parameters[3].SourceColumn = "name";

// Требуется настройка – указать версию поля таблицы

upd_cmd.Parameters[2].SourceVersion = DataRowVersion.Original;

upd_cmd.Parameters[3].SourceVersion = DataRowVersion.Original;

// Помещаем наши команды в адаптер

da.InsertCommand = ins_cmd;

da.DeleteCommand = del_cmd;

da.UpdateCommand = upd_cmd;

После того, как в адаптере определены все команды, можно свободно изменять данные в рассоединенном наборе, а затем обновить их в базе вызовом Update():

DataTable dt = CD_Rent.Tables["Artists"];

DataRow row = dt.NewRow();

row["id"] = 100;

row["name"] = "Uma Thurman";

dt.Rows.Add(row);

row = dt.Rows[1];

row["name"] = "Alex";

da.Update(CD_Rent, "Artists");

Как показывает пример, ручное создание команд для адаптера даже в случае простого набора данных выглядит громоздким (хотя это очень гибкое решение). ADO.NET предоставляет класс для автоматической генерации команд адаптера. Это класс CommandBuilder (класс зависит от поставщика данных, поэтому приведено его «обобщенное» имя).

Работа с классом CommandBuilder происходит следующим образом. Создается объект класса и связывается с определенным адаптером данных, у которого уже задана команда SelectCommand. После установки подобной связи CommandBuilder отслеживает событие обновления строки данных, которое происходит при вызове метода Update(), и генерирует и выполняет необходимые SQL-команды на основе текста команды SELECT.

Приведем пример кода, использующего CommandBuilder. Вот как могла бы выглядеть «генерация» команд для адаптера, с которым мы работали:

// Создаем объект CommandBuilder и связываем его с адаптером

SqlCommandBuilder cb = new SqlCommandBuilder(da);

// И, собственно, все! Можем работать с методом Update()

. . .

da.Update(CD_Rent, "Artists");

Конечно, класс CommandBuilder не «всемогущ». Он генерирует правильные команды обновления, если выполняются все следующие условия:

  • запрос возвращает данные только из одной таблицы;

  • на таблице в базе определен первичный ключ;

  • первичный ключ есть в результатах вашего запроса.

Кроме этого, объект CommandBuilder не предоставляет максимальной производительности периода времени выполнения. Вы можете написать и добавить в код собственную логику обновления за время, меньшее, чем объекту CommandBuilder потребуется, чтобы выбрать и обработать необходимые для создания аналогичного кода метаданные таблицы из БД. CommandBuilder не позволяет управлять генерацией логики. Нельзя указать нужный способ оптимистического управления параллелизмом. Нельзя передавать обновления средствами хранимых процедур.

В таблице 36 приведены свойства и методы класса SqlCommandBuilder.

Таблица 36

Свойства и методы класса SqlCommandBuilder

Имя свойства

или метода

Описание

DataAdapter

Свойство позволяет просмотреть или изменить объект DataAdapter, сопоставленный с объектом CommandBuilder. Значение этого свойства можно задать в конструкторе объекта CommandBuilder

DeriveParameters()

Статический метод. Получает в качестве параметра объект-команду для вызова хранимой процедуры. На основании информации из БД, заполняет коллекцию Parameters команды-параметра

GetDeleteCommand()

Возвращает объект Command с логикой для свойства DeleteCommand объекта DataAdapter

GetInsertCommand()

Возвращает объект Command с логикой для свойства InsertCommand объекта DataAdapter

GetUpdateCommand()

Возвращает объект Command с логикой для свойства UpdateCommand объекта DataAdapter

QuotePrefix

Содержит префикс, используемый CommandBuilder для имен таблиц и столбцов в генерируемых им запросах

QuoteSuffix

Содержит суффикс, используемый CommandBuilder для имен таблиц и столбцов в генерируемых им запросах

RefreshScbema()

Указывает объекту CommandBuilder создать логику обновления заново

5. ASP.NET