Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции_CSharp_4.docx
Скачиваний:
37
Добавлен:
02.11.2018
Размер:
237.61 Кб
Скачать

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

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

SqlDataAdapter da = new SqlDataAdapter();

var CD_Rent = new DataSet("CD_Rent");

da.Fill(CD_Rent, "Artists");

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

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

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. Приведем полный текст создания и настройки команд:

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

var con = new SqlConnection();

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

var ins_cmd = con.CreateCommand();

var del_cmd = con.CreateCommand();

var 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", SqlDbType.Int);

ins_cmd.Parameters.Add("@p2", SqlDbType.VarChar);

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

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

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

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

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

del_cmd.Parameters.Add("@p1", SqlDbType.Int);

del_cmd.Parameters.Add("@p2", SqlDbType.VarChar);

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

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

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

upd_cmd.Parameters.Add("@p1", SqlDbType.Int);

upd_cmd.Parameters.Add("@p2", SqlDbType.VarChar);

upd_cmd.Parameters.Add("@p3", SqlDbType.Int);

upd_cmd.Parameters.Add("@p4", SqlDbType.VarChar);

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().

Как показывает пример, ручное создание команд для адаптера даже в случае простого набора данных выглядит громоздким (хотя это очень гибкое решение). 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 потребуется, чтобы выбрать и обработать необходимые для создания аналогичного кода метаданные таблицы из БД.