Использование объекта создания команд

В предыдущем примере вы увидели, как с помощью мыши и одной таблицы можно создать различные команды и полнофункциональное приложение, не вдаваясь в написание кода. Вы также увидели, что единственной заданной вами командой является SelectCommand, а все другие команды сгенерированы незаметно для вас. Эти команды сгенерированы объектом Builder.

Если посмотреть внимательно, то сгенерированная команда, экземпляр которой можно увидеть в UpaateCommand. CommandText, не очень-то эффективна:

UPDATE

(dbcl.[Animals; SET [Animal-D] -@AnimalID,

(AmrralNarre; = 0AnimalName

WHERE

( ( ;Anmal ID] = @Onginal_AniraallD) AND ((AnimaiNamej =

(jOrLgina.l AnimalNane) )

SELECT AnimallO, AninalMarae FROM Animals

WHF.RE (AnimallD — @Anima];p)

Этот сгенерированный SQL-запрос просто ужасен! Он не только пытается обновлять первичный ключ, так что вам придется очень внимательно следить за тем. чтобы ничего не напутать, но он и сравнивает каждый отдельный столбец в предложении WHERE. В данном случае у нас задействовано только два столбца, но при выполнении UPDATE для совершенной уверенности, что рассмотрены все проблемы параллелизма, сгенерированный код придерживается перестраховочного подхода и при обновлении сравнивает каждый столбец с исходным значением. Конечно, в свойствах TableAdapter можно указать и свои собственные значения текста команды. А для сложных запросов, имеющих дело с несколькими таблицами, вам придется указывать эти команды самостоятельно.

Класс SqlCommandBuilder использует SelectCommand, указанную для непосредственной выборки метаданных из базы данных. Если SelecLComm&r-d изменится, необходимо вызвать метод Ref reshSchema для объекта SqlCommandBuildor. чтобы обновить остальные команды. Критерием работы компонента создания команд является то. что указанная Se] ectCommand должна работать только с одной таблицей, а в выбранных результатах должен обязательно присутствовать первичный ключ или уникальный столбец. При невыполнении этих условий будет возбуждено исключение InvalidOperationException. Конечно, в ADO.NET, как и в жизпн, ничего не бывает бесплатно, гак что для генерации этих запросов нужны дополнительные уенлня, обычно в внде дополнительного запроса к схеме базы данных.

По умолчанию CommandBuilder не генерирует очень эффективные запросы, а применяет перестраховочный подход, сравнивая все столбцы при выполнении операций UPDATE. INSERT или DELETE. Это, видимо, самый безопасный и наиболее переносимый между СУБД подход, т.к. в нем не используются никакие возможности отдельных СУБД {вроде отметок времени в Microsoft- SQL Server}. Но все же это, наверно, самый неэффективный подход — просто нз-за чрезмерной сложности предложения WHERE.

Оказывается, у объекта CommandBuilder имеется свойство ConflictOption, которое позволяет указать, что такой безопасный и не эффективный поход не нужен. Эта новая возможность появилась в .NET 2.0. Рассмотрим ее на примере. Код этого консольного приложения можно загрузить с web-сайта издательства под именем Exercise 9.2, но можно и создать его. выполнив следующие шаги:

1. Вначале создайте консольное приложение и назовите его Exercise 9.2. Кроме того, создайте таблицу с именем Animal s2, со структурой, похожей на структуру из упражнения 9.1, но с дополнительным столбцом Timestarrp {Отметка времени). SQL-сценарий создания этой таблицы находится в загружаемом коде упражнения Exercise 9.2.

2. Цель данного упражнения — поэкспериментировать с классом Cor.mand-Вит lder. Есть два способа создания объекта CommandBuilder. Первый способ — использование стандартного конструктора с последующим указанием DaLaAdapter в качестве свойства. Второй способ — передача DataAdapter через параметр конструктора. В нашем примере, чтобы уменьшить объем ввода с клавиатуры, применяется второй подход:

C#

SqlDataAdapter myDataAdapter = new SqlDataAdapter(«Select “ from Animals2”, connecticrStrirg); SqlCommanaBuilder cmdBlar = new SqlCommandBuilder(TtyDatsAdapter);
VB.NET

Dim myDataAdapter As SqlDaLaAaapter

New SqlDataAaapter(«Select * from Aniitdls2″, ccnnectj onS-nng)

Dim cndBldr As SqlComrrandBuilder — New 3qICcmrr,aad3uildei (TiyDats.Mapv.o:)

3. Вот и все. Сейчас нужно лишь проверить различные команды, сгенерированные компонентом создания команд. Для примера рассмотрим команду обновления:

C#

Console.WriteLine(«Команда обновления = «);

Console .WriteLine (cmcffilar. GetOpdateCon-nand () . Conwiap.dText) ;

VB.NET

Console .Writ.eLme («Команда обновления — «)

Console .WriteLine (omdBldr . GetUpdateComraand () . Command’lext)

4. Откомпилируйте и запустите приложение. Просмотрите текст команды, сгенерированный методом GetUpdateCommand. Результат должен выглядеть примерно так:

UPDATE ;ДП1 roals2J SET lAnimlPZO] — @pl, [AnimalKsme] = Эр?

WHERE ( ( LAnimal2lD]= ЭрЗ) AND ( (AnimalNameJ — @p4) )

5. Как видно из параметров рЗ и р4, адаптер данных должен заменять первоначальные значения столбцов, поэтому сгенерированная команда проверяет во время обновления все столбцы, чтобы лишний раз убедиться в отсутствии конфликта параллелизма. Теперь изменим этот код и добавим в него дополнительную стоку, в которой устанавливается значение для свойства Conf 1 ictOption компонента создания команды. Кроме того, измените стандартное значение Conf 1т с t Opt ion с Compa г eAUSear enable Values на CompareRowversion. Полный полученный код приведен ниже:

C#

SqlDataAdapter myDataAdapler = new SqlDataAdapter («Select. * from Amrrals?», connectionString); SqlCommandBuilder cmdBldr = new SqlCo-пшапаЗи:iaer(nyDataAdapterj ; cindBldr.ConflictOption = ConflietOption.CompareRowversion;

Console. Write!,ine («Команда обновления = •’) ;

Console .Write-Line (cmdBldr. GetUpaateComir;ana () . Comrnana’lext) ;

VB.NET

Dim myDataAdapter As SqlDataAdapter =

New SqlDataAdapter C’Sele-t * fro-n Mimals2″, connectionString)

Dim cmdBldr As SqlComnanciBuiider = New SqlCommandBui: der (‘nyDataAdaptcr) cindBldr.ConflletOption = Conf lictOption. CompareRowversion

Console.WriteLane(«Команда обновления — «)

Console .WriteLine (cmdB] dr . C.etUpd.dteComn-an’i () . CommandText)

6. Откомпилируйте и запустите приложение, а затем еще раз просмо!рите текст команды обновления. Вы должны получить следующие результаты:

UPDATE [Aninals2] SET [Ammal2ID] = dpi, [AnimalName] = @p2 WHERE (([Timestamp] = @p3) )

Учтите, что если бы в таблице не было типа данных Timestamp, то было бы возбуждено исключение. Столбец типа Timestamp в SQL Server — это специальный столбец, изменяющий свое значение при каждом изменении значения строки; то есть это замечательный и эффективный способ избежать конфликтов параллелизма.

7. Теперь замените значение Conf lictOption на OverwriteChanges и просмотрите текст команды обновления. Он должен быть таким:

UPDATE [Animals2j SET [Animal2lD] = @pl, [AnimalName] = @p2 WHERE (( [Ammal2ID] = @p3) )

В этом случае компоненту создания команд указывается, чтобы он не заботился о параллелизме и перезаписывал предыдущие значения; таким образом, в операторе осталась только проверка первичного ключа в предложении WHERE.

Теперь вы видите, как можно использовать объект CommandBuilder для генерации запросов, а, кроме того, вы получили дополнительную возможность использовать свойство Conf]. т ctOption. что недоступно в чистом подходе перетаскиванив мышью. Здесь еще раз становится понятно, что слой данных эффективен и чист настолько, сколько усилий в него вложено. Но все же наилучшим решением в приложении уровня предприятия с высокими требованиями может быть более трудоемкий подход, связанный е утомительным написанием всего SQL самостоятельно. Понятно, что 500 специалистов, перетаскивающих 2000 таблиц в промышленном приложении, вряд ли являются хорошим решением.

А теперь еще раз взглянем на листинги, а именно, на следующую часть:

C#

this.Adapter.Update(dataset, «Animals»)

VB.NET

Me.Adapter.Update(dataSet, «Animals»)

Как уже было сказано, класс DataAdapter выступает связником между подключенным и автономным мирами. Кроме того, в главе 6 было показано, что объекты DataSet и DataTable не только хранят данные в автономном кэше, но и ведут историю изменений, выполненных с данными. И вот эта информация истории изменений позволяет вышеуказанному простому и короткому оператору решить, какое действие необходимо выполнить для каждой строки каждой таблицы из DataSet.

Related posts:

Определение правильного размера пула
Класс DataSet
Создание типизированных DataSet в Visual Studio .NET
Вы можете оставить комментарий, или ссылку на Ваш сайт.

Оставить комментарий