Книги, научные публикации Pages:     | 1 |   ...   | 3 | 4 | 5 | 6 | 7 |   ...   | 8 |

David Sceppa Microsoft Press Дэвид Москва 2003 Р Г Г If Р F г V А Л А - л г УДК 004.45 32.973.26-018.2 С28 Д. ...

-- [ Страница 5 ] --

Следующий фрагмент кода демонстрирует, как работает метод 8 Сортировка, поиск, фильтрация Visual Basic Dim As New Dim tblCustomers, As DataTable = tblOrders = Dim As = New vueOrders = C# DataSet ds = DataSetO;

DataTable tblOrders;

tblCustomers = tblOrders = DataView vueCustomers, vueOrders;

= new vueOrders = Примечание При создании нового объекта DataView с помощью метода свойство возвращает пустую Тем не менее при этом доступны только нужные дочерние записи. Как это Новый объект DataView использует функцию, которую технические * предпочитают называть Метод Delete Метод Delete объекта позволяет удалить запись. что запись лишь помечается как удаленная, на самом деле она по-прежнему объекте DataTable. Чтобы реально удалить запись из объекта DataTable, вызовите или передайте ожидающие изменения в БД с помощью объекта Вопросы, которые стоит задавать почаще Вопрос. Как выбрать наиболее подходящий метод для поиска данных в объекте Ответ. Это от что вы хотите искать и что собираетесь делать с ре зультатами поиска. Бот несколько рекомендаций:

31 8 Часть III Автономная работа с данными: DataSet модели Х для поиска записи по значению первичного ключа используйте метод Х чтобы связать элементы управления с записями, удовлетворяющими критери ям поиска, применяйте объект Х для многократного поиска по не являющимся ключевыми, исполь зуйте метод Х во всех остальных случаях годится метод Вопрос. Создать объект DataView в коде достаточно просто. А можно ли полу чить какие-нибудь преимущества, создавая этот объект средствами Visual Studio в период разработки?

Ответ. Я рад слышать этот вопрос. Создавая объекты DataView с помощью Visual Studio вы получаете два главных преимущества. Во-первых, если объект DataView будет использоваться связанными элементами управления, то созданные в период разработки элементы и объекты можно сразу же связать. Лично я пред почитаю задавать нужные свойства в период разработки, щелкая мышью, а не набирая код.

За Visual Studio действительно создает объект DataView, что дает вам второе преимущество. Когда вы работаете с окном Properties, Visual Studio изменяет значения соответствующих свойств объекта DataView. Если вы не корректно задали значение свойства, например сделали опечатку в имени столб ца или указали отсутствующий в DataTable Visual Studio оповестит вас об этом. Хотите Ч хотите Ч нет, но информация в опове щении по-настоящему полезна (рис. 8-2). Если вы сделаете аналогичную ошибку непосредственно в коде, он будет успешно скомпилирован, и ошибка проявится только при запуске приложения.

Рис. 8-2. генерируемое Visual Studio в период разработки при неверном вводе столбца Вопрос. Зачем нужен объект Ответ. Ч это контейнер с объектами которые в целом аналогичны объектам но предоставляют более ограниченную функциональность. Я не могу представить реальной ситуации, когда объект Data ViewManager окажется более полезным, чем DataView, и поэтому не рассматриваю его в книге.

ГЛАВА 8 Сортировка, фильтрация Вопрос. Как мне найти запись в объекте DataView, если поиск должен осуществ ляться по столбцу, не указанному в свойстве Sort этого объекта?

Ответ. Это довольно распространенная ситуация, возникающая при работе со элементами управления Windows-формы. К сожалению, ни ни объекты связывания с данными, предоставляемые Windows-формой, не пред лагают изящного решения этой проблемы.

у вас есть сетка, связанная с элементом управления DataView, который отображает отсортированные по столбцу сведения о клиентах.

Вам требуется предоставить возможность поиск клиентов по другому например ContactName. На рис. 8-3 показан пример такой формы.

Рис. Отсортированные по столбцу Country сведения о клиентах Наша задача Ч соответствующую запись в сетке. На рис. 8- поля ContactName текущего ряда Ч Aria Cruz, а поля Country Ч Brazil.

Нам нужно определить порядковый номер требуемой записи в объекте т. е. найти эту запись в объекте DataTable. Это можно сделать двумя способами:

посредством метода Select объекта DataTable или метода Find объекта DataView.

Остановимся на последнем.

Нам требуется объект DataView, значение поля Sort которого Ч ContactName.

Затем с помощью метода Find удается определить порядковый номер нужной за писи в объекте DataView. Зная номер записи, мы обратимся к нужному объекту и узнаем значение поля Country, Вот фрагмент кода, который все это осуществляет:

Visual Basic Dim As Dim vueByCountry As New DataView(tbl) = "Country" Dim As = "ContactName" Dim As Integer 320 Часть Автономная работа с данными: объект модели = - = Dim As = Row Dim As String = Visual C# DataTable = new = new DataView(tbl);

= "Country";

DataView vueByContactName = new vueByContactName. = intlndexCountry, = -1;

intlndexContactName = = strCountry = Это самая простая часть. все усложняется.

этап Ч вызов метода Find объекта содержащего по столбцу Country о клиентах. Тем не менее несколько за писей могут иметь одинаковое значение поля Country, так что это далеко не конец.

поля Country не обязательно уникальны, и поэтому имеет смысл вос методом объекта DataView. Метод возвращает массив объектов Один из элементов этого массива представляет нужную нам запись, однако определить порядковый номер объекта и поэтому метод FindRows к сожалению, не поможет.

Еще один вариант Ч целиком сканировать объект DataView, пока не отыщется нужная Код для такого поиска очень прост, но неэффективен.

На рис. 8-3 показано несколько записей, имеющих одинаковое с нужной нам записью значение поля Country. Есть еще один недостаток использования метода в данной ситуации Ч нет гарантии, что возвращенный порядковый помер соответствует первой записи объекта DataView, удовлетворяющей крите рию поиска.

Итак, получив порядковый номер клиента из той же страны (Country), что и нужный мам клиент, мы можем просмотреть список всех остальных клиентов из этой страны. Приведенный ниже код использует возвращенное значение метода в качестве отправной точки и перемещается вперед по объекту DataView, пока не нужный ряд, не переместится за пределы объекта DataView или не пе рейдет к записи, значение поля Country которой отличается от нужного нам зна нужный ряд не отыщется, код проверит ряды, которые предшествуют ряду, ставшему отправной точкой. Нельзя что данный код изящен;

тем не менее он максимально эффективен, учитывая, что объект DataView не предназ для таких ситуаций.

Basic As Integer = Dim As Integer = ГЛАВА 8 Сортировка, фильтрация Do If Is Then = Exit Do End If intCounter += Loop While intCounter < And = strCountry If Not blnFound Then intCounter = - Do intCounter >= 0 And = strCountry If Is row Then intlndexCountry = intCounter blnFound = True Exit Do End If intCounter -= Loop End If If blnFound Then Else End If Visual C# int intStartingPoint = while < { if row) intlndexCountry = intCounter;

blnFound = true;

break;

} intCounter++;

i if { intCounter = intStartingPoint - 1;

while (intCounter >= { if == row) 322 Часть III Автономная работа с данными: объект DataSet модели i = = true;

break;

) if (blnFound) else Работа с объектами DataSet со строгим контролем типов О предыдущих трех главах рассказывалось о создании и использовании объек тов DataSet. Как показано код для доступа к содержимому DataSet в программ ном плане аналогичен коду для доступа к объектам, которые использовались ше, например к объекту Recordset моделей ADO и ADO.NET и Visual Basic = ADO.NET и Visual C# = ADO, DAO и Visual Basic = Разработчики писали подобный код, начиная с первых дней Visual Basic. Технически он безупречен Ч работает отлично. Тем не менее это не значит, что усовершенствовать старые методики программирования нельзя.

Чтобы упростить написание кода для доступа к данным, в Microsoft Visual Studio объекты DataSet со строгим контролем типов. Теперь возможен такой код:

Visual Basic = CompanyName 324 Часть Автономная работа с данными: объект DataSet модели Visual C# = Объект DataSet со строгим контролем типов можно рассматривать как объект DataSet с классом. В частности, объект DataSet со строгим контролем Ч это класс, наследующий от класса DataSet и включающий свойства и основанные на указанной схеме. Кроме того, этот содержит другие классы для объектов и Ч они позволяют создавать более эффективный код доступа к данным.

Создание объектов DataSet со строгим контролем типов Так как же создать класс DataSet со строгим контролем типов? Можно воспользо ваться одним из стандартных способов. Например, написать код и воспользоваться утилитой командной строки из состава Framework SDK. Или же, что проще, применить метод, связанный со средой разработки Visual Studio Ч он не требует открытия окна командной строки.

Сложный способ В состав Framework SDK входит утилита командной строки под названием XML Schema Definition Tool, позволяющая генерировать файлы классов на основе файлов XML-схем Совместно используя эту утилиту и метод объекта DataSet. вы сможете преобразовать свой объект DataSet в од ноименный класс со строгим контролем типов, Использование метода объекта DataSet Из раздела главы 6, посвященного созданию объектов DataSet в среде разработки Studio вам известно, что Studio добавляет в проект файл с расширением.xsd. Этот файл содержит информацию схемы (таблицы, столбцы, ограничения и отношения) для объекта DataSet, и можно создать программ но, при помощи метода объекта DataSet.

Метод WriteXmlScbema перегружен и принимает объект Stream, или имя файла в виде строки. Следующий фрагмент кода создает объект DataSet столбцов таблиц Customers и Orders БД Кроме прежде чем записать схему для объекта DataSet в файл, код создает объект связывающий два объекта DataTable.

Visual Basic Dim As String = 4 _ "Initial Dim As New strSQL = "SELECT Phone & Customers" As New StrSQL = "SELECT OrderDate & ГЛАВА 9 Работа с объектами DataSet со строгим контролем типов "FROM Orders" Dim As New Dim ds As New = "Orders") Visual C# string strSQL;

= Source=(local)\\NetSDK;

+ "Initial strSQL = "SELECT CustomerlD, ContactName, Phone + "FROM Customers";

daCustomers = new strConn);

StrSQL = "SELECT CustomerlD, OrderDate " + "FROM Orders";

OleDbDataAdapter daOrders = new strConn);

DataSet ds = new DataSetO;

= cn.OpenO;

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

Использование утилиты XML Schema Definition Tool Утилита XML Schema Definition Tool Ч это обычный исполнимый файл с XSD.exe, хранящийся в папке bin и позволяющий генерировать файлы классов на основе файлов XML-схем (.xsd- или.xdr-файлов). Кроме того, утилита способна генерировать файлы схем на основе библиотек и исполнимых файлов (.ехе).

В показанном выше фрагменте кода мы сохраняли схему объекта в файл. Теперь с помощью утилиты XML Schema Definition Tool мы сгенерируем на 326 Часть III Автономная работа с данными: объект DataSet модели основе этого файла схемы файл класса. Откройте окно сеанса MS-DOS и набери те в окне командной строки следующее:

Visual /d /1:VB Visual C# C:\>XSD / Примечание Чтобы открыть окно командной строки, раскройте меню Programs\Accessories и выберите Command Prompt. Есть и другой спо соб Ч раскрыть меню Start, выбрать Run и затем ввести cmd.exe.

Примечание Можно указать полный путь к файлу XSD.exe или добавить путь к папке bin комплекта средств разработчика Framework в состав переменной среды Path. Кроме того, необходимо указать путь к файлу схемы.

Первый параметр Ч это путь к файлу XML-схемы. Второй параметр указывает, что который требуется создать, от класса DataSet. В примере для Visual Basic используется третий параметр, определяющий язык файла По умолчанию утилита файлы классов Visual С* У утилиты XML Schema Definition есть также и другие параметры. Они описаны в документации Framework SDK;

кроме того, их можно набрав в командной строке XSD Теперь добавим новый файл класса в проект и создадим экземпляр вашего нового класса DataSet со строгим контролем типов:

Visual Basic Dim As New Chapter9() Visual C# = new Chapter9();

Примечание Имя вашего класса зависит от свойства объекта использованного при создании.xsd-файла. Во фрагменте кода, сгенерировавшем наш значение DataSetName Ч оно и станет именем нашего нового класса DataSet со строгим кон тролем типов, Простой способ По сравнению с способом создать класс DataSet со строгим контро лем типов в Visual Studio гораздо проще. Вам не придется писать какой-либо код и, что самое главное, вводить в окне сеанса MS-DOS.

ГЛАВА 9 Работа с объектами DataSet со строгим контролем типов Чтобы убедиться в этом, создадим класс, аналогичный созданному ранее. Но вый класс DataSet, как и предыдущий, включает в себя два объекта и объект Для начала создайте приложение Microsoft Windows на удобном вам языке.

воспользуйтесь вкладкой Data панели инструментов и добавьте на объекта С помощью мастера Data Adapter Configuration свяжите оба объекта с БД В окне Generate SQL State ment мастера введите следующие SQL-операторы:

SELECT Phone FROM Customers SELECT CustomerlD, Щелкните в свободной области конструктора правой кнопкой и выберите Generate Dataset. Откроется диалоговое окно Generate DataSet (рис. 9-1). Введите имя нового класса DataSet. и щелкните ОК.

Generate а that the tables.

Choose a to add to the V. Customers V Orders (OleDbDataAdapter Che Рис. Создание нового класса DataSet со строгим контролем типов И вот вы одним махом создали сразу файл схемы DataSet и класс DataSet со строгим контролем типов. Единственное отличие класса созданного сейчас, от класса, созданного нами ранее, Ч то, что у первого нет объекта DataRelation.

Пока нет.

В окне Solution Explorer дважды щелкните файл схемы. В окне XML Schema Designer щелкните таблицу Orders правой кнопкой и выберите Relation.

Откроется диалоговое окно Edit Relation (рис. 9-2). Не изменяя значения по умол чанию, щелкните ОК.

Вот и все. Ни ни суеты. Больше делать ничего не требуется. Теперь можно создавать экземпляры нового класса DataSet со строгим контролем типов в коде, как показано выше, или в период разработки при помощи элемента DataSet вкладки Data панели инструментов.

328 Часть III Автономная работа с данными: DataSet модели the element and to ftsM, Рис. 9-2. Добавление объекта в класс DataSet Чтобы создать новый класс DataSet со строгим контролем типов, среда Visual Studio выполнила ряд действий.

1. Создала новый экземпляр класса DataSet.

2. Вызвала методы FillScbema всех объектов выбранных в диалого вом окне Generate Dataset. чтобы добавить информацию схемы в новый класс DataSet, 3. Вызвала метод класса DataSet.

4. Добавила в проект 5. При помощи утилиты XML Schema Definition Tool создала на основе.xsd-фай ла класс DataSet со строгим контролем типов.

6. Добавила новый файл класса в проект.

Где же находится файл класса?

Так где же находится файл вашего класса DataSet со строгим контролем типов?

Внимательно посмотрев, вы увидите в верхней части окна Solution Explorer па нель инструментов. Значок на одной из кнопок этой панели изображает множе ство файлов. Если подвести к этой кнопке мышку, всплывет подсказка Show All Files (отображать все файлы). Щелкнув кнопку, вы увидите древовидную отображающую все скрытые файлы вашего проекта.

С файлом схемы DataSet (Chapter9.xsd) связаны еще два файла. Первый Ч это файл класса DataSet со строгим контролем типов;

его имя Chapter9.vb или ter9.cs Ч в зависимости от выбранного вами языка. Второй файл имеет расшире ние.xsx;

это просто текстовый файл с параметрами структуры класса DataSet, утилитой XML Schema Definition Tool.

На самом деле файл класса включает несколько классов. Есть основной класс, происходящий от DataSet. Он предоставляет два объекта DataTable Ч Customers и Orders, каждый из которых возвращает класс, происходящий от DataTable. Оба этих ГЛАВА Э Работа с объектами DataSet со строгим контролем типов класса (CustomersDataTable и предоставляют по умол чанию возвращающее специфичный для таблицы класс, происходящий от DataRow.

Visual Basic Dim As New Dim As = Dim As = Visual C# ds = new Chapter9();

tblCustomers = = Использование объектов DataSet со строгим контролем типов Объекты DataSet со строгим контролем типов упрощают процесс облегчая написание кода для доступа и изменения содержимого DataSet.

Рассмотрим несколько которые позволят сравнить работу с данными при помощи обычных объектов DataSet и объектов DataSet со строгим контро лем типов, Добавление записи Все соответствующие объектам из состава DataSet, позволяют добавлять запись в новый экземпляр DataTable двумя способами. Метод возвращает для вашего экземпляра новый объект DataRow со строгим контролем типов. Затем вы можете воспользоваться свойствами это го объекта и задать значения полей записи, показано ниже, Visual Basic Dim ds As New Dim tblCustomers As = Dim rowCustomer As rowCustomer = rowCustomer. = = "New = "New Contact" = "(800) 555-1212" rowCustomer) Dim rowCustomer As DataRow = = "ABCDE" = "New Company" = "New Contact" Часть (II Автономная работа с данными: объект DataSet модели = 555-1212" C# Chapter9 ds = new tblCustomers = rowCustomer = "ABCDE";

= "New Company";

= "New Contact";

= "(800) 555-1212";

DataRow rowCustomer = = "ABCDE";

= "New Company";

= "New Contact";

= "(800) 555-1212";

Если просто смотреть на код в книге, преимущества использования объектов DataSet со строгим контролем типов Открыв код в среде разработки Visual вы четче поймете, чем же такие объекты.

Took Help " End Sub End Рис. Использование объекта DataSet со строгим контролем типов в коде в Visual Studio На рис. 9-3 показан снимок среды разработки. Как видно, все поля объекта DataRow со строгим контролем типов перечислены в списке автодополнения. Когда в Visual Basic 5 появилось автодополнение операторов, я трепетал от радости. Тем ГЛАВА 9 Работа с объектами DataSet со строгим контролем типов не менее, если в коде на Visual Basic 6 и ADO 2.x я неправильно вводил имя поля, ошибка лишь при запуске приложения. Объекты DataSet со строгим контролем типов и функция автодополнения операторов позволяют избавиться от таких проблем еще на стадии разработки.

Как и Add набора метод DataTable со строгим контролем типов перегружен. В следующем фрагменте кода используется объект с именем Customers из состава созданного нами ранее объекта DataSet:

Visual Basic Dim As New Dim tblCustomers As = "New "New Contact", "(800) 555-1212") "New Company", _ "New Contact", "(800) Visual C# Chapter9 ds = new tblCustomers = "New "New Contact", "(800) "New Company", "New Contact", "(BOO) Благодаря технологии и автодополнению операторов, эта функция выглядит все более и более впечатляющее по мере как вы пишете код. В Visual Studio имена параметров метода автоматически отображаются при вводе текста, и вам не требуется к другим частям кода и искать имена и порядок полей.

Поиск записи В случае с обычным объектом DataSet для поиска записи по ее первич ного ключа можно воспользоваться методом Find набора Rows объекта Иногда метод Find неудобен, особенно если для объекта DataTable определен пер вичный ключ, состоящий из нескольких полей. первичный ключ таб лицы Order Details БД wind включает поля и Код поиска записи в соответствующем объекте использующий метод будет выглядеть так:

Visual Basic Dim tblDetails As DataTable Dim rowDetail As DataRow rowDetail = 332 Часть III Автономная работа с данными: DataSet модели Visual C# DataTable tblDetails;

object[] Данный код но. создавая его, можно запутаться. И что более важно, можно запутаться при его чтении, а это затрудняет его поддержку.

Если для объекта DataTable определен первичный ключ, соответствующий класс DataTable из класса DataSet со строгим контролем типов предоставляет собственный метод Find. Добавив в наш класс DataSet класс DataTable, соответству ющий таблице Order мы сможем заменить предыдущий фрагмент кода следующим, более простым в поддержке и написании:

Visual Basic Dim As New Dim tblDetails As т ds.Order_Details Dim rowDetail As rowDetail = 7} If Is Nothing not Else & End If Visual Chapter9 ds = new tblDetails = ds.Order_Details;

rowDetail;

rowDetail 7);

tf (rowDetail == null) not else + + + Редактирование записи Редактирование записи объекта DataSet со строгим контролем типов аналогично редактированию записи обычного объекта DataSet. Вы точно так же можете при менить методы и Кроме того, вы сможете обращать ся к значениям полей объекта используя свойства объекта DataRow со строгим контролем типов:

ГЛАВА 9 Работа с DataSet со строгим контролем типов Visual Basic Dim ds As New Dim As = = "Modified" = "Modified" Visual Chapter9 ds = new Chapter9();

rowCustomer = = "Modified";

= Работа со значениями NULL На этапе тестирования бета-версии Visual Studio в нескольких группах но востей интенсивно обсуждались значения NULL. Многие разработчики не пони мали, как изменить значение поля на NULL или как определить, содержит поле такое значение. Как вы помните из главы 6, функция объекта DataRow по зволяет проверить наличие значения а метод Ч задать полю такое значение.

Объекты со строгим контролем типов также упрощают работу со зна чениями NULL. Каждый объект DataRow со строгим контролем типов предостав ляет два метода: один из них проверяет, содержит ли поле значение NULL, дру гой позволяет задать полю такое значение. В следующем фрагменте кода исполь зуется поле и поэтому соответствующие методы названы и Visual Basic Dim ds As New Dim rowCustomer As = ds.Customers(O) содержит ли поле ContactName Null If Then name is Null") Else name: & End If полю ContactName значение Null of 334 Часть III Автономная работа с данными: объект DataSet модели If = Visual C# Chapter9 = Chapter9();

= содержит ли поле Null if is else name: + полю ContactName значение Null rowCustomer.

if = Работа с данными Объект DataRow предоставляет метода для перемещения по данным Ч и Они принимают либо имя объекта Data на который вы хотите либо сам объект. Если ваш объект DataSet со строгим контролем типов содержит объекты утилита XML Schema Definition Tool добавит методы, перемещаться по иерархичным дан ным без указания объекта DataRelation или его имени. В наш объект DataSet мы добавили объект DataRelation, объекты DataTable Customers и Orders.

После того как мы сохранили изменения.xsd-файла объекта DataSet, Schema Definition Tool добавила в класс объекта DataTable Customers метод а в класс DataRow объекта DataTable Orders Ч метод GetCusto Следующий фрагмент кода с помощью метода GetOrdersRows выводит список всех клиентов и размещенных ими заказов.

Basic Dim ds As New Chapter9{) Dim rowCustomer As Dim As For rowCustomer In for & ГЛАВА 9 Работа с объектами DataSet со строгим контролем типов For Each In & & - & Next rowOrder Next rowCustomer Dim rowCustomer, rowOrder As Each rowCustomer for & For Each rowOrder In C# ds = new Chapter9();

CustomersDataTable tblCustomers = CustomersRow rowCustomer = foreach rowOrder { + foreach rowOrder in Console. + rowOrder. OrderlD. + - + foreach (DataRow rowCustomer in for + foreach (DataRow rowOrder in I Прочие возможности объектов DataSet, и DataRow Классы со строгим контролем типов, генерируемые утилитой XML Schema Definition происходят от классов DataSet. DataTable и DataRow. Следовательно, с можно как с обычными классами без контроля типов.

Так, у классов DataSet со строгим контролем типов нет собственных методов для чтения/записи схемы. Но поскольку эти классы происходят от класса DataSet, они все равно предоставляют методы и Однако во всех других случаях, например при получении данных или передаче изменений с помощью объекта объект DataSet со строгим контролем типов аналогичен обычному одноименному объекту.

336 Часть III Автономная работа с данными: объект DataSet модели Когда стоит использовать объекты DataSet со строгим контролем типов Все разработчики, с которыми я общался в Microsoft и других компаниях, отме чают, насколько сильно объекты DataSet со строгим контролем типов упростили процесс разработки. Тем не менее многие из них весьма ограниченно использу ют такие объекты DataSet в приложениях.

Они не видели код, генерируемый утилитой XML Schema Definition Tool, и не сравнивали производительность объектов DataSet со строгим контролем типов и без него. И знаете что? Я сам был настроен скептически, пока не начал работать над этой главой (о том, что я открыл для Ч чуть позже).

Программные компоненты и швейцарские армейские ножи Лет десять или двадцать назад я был бойскаутом. По неписаному правилу, каждый бойскаут брал с собой в походы швейцарский армейский нож. Такие ножи очень удобны Ч и консервы открыть на привале, и палочки обстругать вечером, у костра.

Разные модели ножей различаются по в одних лезвий больше, в других меньше. К счастью, по крайней мере у одного мальчишки из нашего отряда с собой всегда был нож с пинцетом Ч ведь если совместить маль чишек и дерево, занозы неизбежны. В какой-то момент и мне подарили нож бо лее чем с двумя десятками лезвий. В нем было все, кроме валика для мойки ветро вого стекла машины. Постоянно я пользовался двумя или тремя лезвиями, осталь ными же Ч очень редко, так как они были мне не нужны или плохо работали.

Еще одно неписанное правило гласит: дети теряют маленькие предметы, и вероятность потери пропорциональна стоимости предмета. Это правило в пол ной мере распространялось и на нас. К концу похода по крайней мере один от важный бойскаут обнаруживал, что лишился ножа. Потеряв в очередном походе свой нож, я купил удобный, но более простой. Он хранится у меня и сейчас. Не могу сказать, что часто им однако открывалка и штопор сейчас мне требуются чаще, чем в 12 лет, Почему же я не приобрел на еще один нож с кучей лезвий? Все очень просто.

Он стоил дороже и больше весил. Однако я не купил и самую простую модель.

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

Все сказанное верно и для программных компонентов. Простые компоненты обычно работают быстрее, чем предоставляющие множество функций.

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

Преимущества периода разработки Как уже говорилось, самое очевидное преимущество периода разработки объек тов DataSet со строгим контролем типов Ч то, что благодаря технологии и автодополнению операторов в Visual Studio писать код для доступа к со ГЛАВА 9 Работа с объектами DataSet со строгим контролем типов объекта DataSet со строгим контролем типов гораздо проще, чем ана код для обычного объекта DataSet.

Кроме того, приходится писать меньше кода, поскольку код инициализации класса DataSet со строгим контролем типов включает код для создания схемы и необходимых объектов DataTable, DataRelation и Constraint. В объект DataSet без контроля типов добавить информацию схемы можно тремя способа ми: написать нужный код самостоятельно, загрузить схему из.xsd-файла с помо щью метода объекта DataSet или воспользоваться методом FfflSc.be та объекта трудоемкий из этих трех способов Ч тот, что подразумевает применение метода (поскольку для использования ме тода требуется создать Однако в 5 я уже расска зывал, почему данный метод не годится для кода.

При создании или Web-приложения, использующего связывание с связать элементы управления с данными в период выполнения окажет ся гораздо проще, если вы задействуете объект DataSet со строгим контролем типов.

Почему? Такой объект содержит собственную информацию схемы, и Visual Studio предоставит вам список таблиц и полей, с которыми можно связать элемент управления.

На рис. 9-4 показан пример. Выбрав объект DataRelation Customers-Orders, мы можем указать, что нам нужно просмотреть лишь заказы текущего клиента. Заметьте:

сетка отражает структуру объекта Orders. Создавая этот пример, я не написал какого-либо кода.

Рис. 9-4. Связывание с данными при помощи объекта DataSet со строгим контролем типов Объекты DataSet со строгим контролем типов дают преимущества в период выполнения и разработчикам многоуровневых приложений. ссылку на библиотеку классов или Web-сервис, возвращающий объект DataSet со 338 Часть III Автономная работа с данными: объект DataSet модели гам контролем у проекта будет собственная копия и файла класса, соответствующих классу DataSet со строгим контролем типов. Таким образом, по-прежнему может использовать преимущества перио да разработки, предоставляемые объектами DataSet со строгим контролем типов, Преимущества периода выполнения Как объекты DataSet со строгим контролем типов проявляют себя в период вы полнения? Как они сказываются на производительности ваших Код для к содержимому объекта DataSet со строгим контролем типов не только более прост в написании, но и повышает производительность приложения. Ниже показан стандартный способ назначить содержимое столбца текстовому полю с использованием обычного объекта DataSet и объекта DataSet со строгим контро лем ТИПОВ:

Visual Basic контроля типов Dim dsUntyped As New и заполняем DataSet = строгим контролем типов Dim As New Chapter9() DataSet = Visual C# контроля типов DataSet dsUntyped = new DataSetO;

и DataSet строгим контролем типов Chapter9 dsTyped = new Chapter9();

DataSet.

= Производительность кода со строгим контролем типов выше. Насколько? На мо мент написания этой книги была доступна только бета-версия Visual Studio а оценивать производительность бета-версий программных продуктов всегда очень сложно. Тем не менее ряд проведенных мной тестов показал, что в показанном выше фрагменте производительность кода со строгим контролем типов почти в два раза выше производительности обычный объект DataSet, ГЛАВА 9 Работа с DataSet со строгим контролем типов Как же объект DataSet со строгим контролем типов обеспечивает повышенную производительность? В главе 6 мы говорили, что объекты DataRow обращаться к содержимому поля путем указания имени этого поля, порядкового номера или собственно объекта Код, использующий реальный DataColumn, обеспечивает самую производительность, его сложнее всего писать и поддерживать. При передаче имени поля в виде строки написание и поддержка кода упрощаются, но очень сильно падает Код, генерируемый утилитой XML Schema Tool, берет лучшее от обоих этих вариантов. Код, который пишете вы, удобно поддерживать, а код, генериру емый утилитой, использует объект DataColumn. На рис. 9-5 показан код, который XML Schema Definition чтобы возвращать значения поля записей таблицы Благодаря чудесам копирования и вставки на рисунке показан код, генерируемый как в Visual Basic так и в Visual Studio { Рис. Код из класса DataSet со строгим контролем типов, обращающийся к полю записей таблицы Customers Сравнимой производительности можно обращаясь к содержимому поля с использованием объекта DataColumn:

Visual Basic Dim As New и объект OataSet Dim As DataTable = ссылку на нужное поле Dim colCompanyName As DataColumn colCompanyName = Dim As DataRow = = String) 340 Часть III Автономная работа с данными: DataSet модели Visual DataSet = new и DataSet DataTable = ссылку на нужное поле = = = Здесь отметить вот что. В коде класса DataSet со строгим типов нет ничего, что нельзя написать Фактически все, что лумеет класс DataSet со строгим контролем вы можете делать сами. Тем не менее на написание кода требуется время. Чтобы к содержимому обычного объекта DataSet так же эффективно, как к содержимому объекта DataSet со стро гим контролем избегайте в наборах поиска. Вместо этого используйте поиск по индексу или в объекте DataSet без контро ля типов ссылки соответствующие поля.

В тесте, показанном производительность кода, использующего объект DataSet со строгим контролем почти в два раза выше производительности кода на основе обычного объекта DataSet. Я слегка изменил код, и теперь он об ращается к содержимому записи, соответствующий объект кроме того, я добавил нужный код преобразования типов. Что же получилось в итоге? Производительность нового, усовершенствованного кода, обращавшегося к содержимому обычного объекта DataSet, оказалась на выше производи тельности кода, обращавшегося к объекту DataSet со строгим контролем типов.

Использование объектов DataSet со строгим контролем типов создает допол нительную нагрузку, и поэтому создание, заполнение и доступ к ним требуют больше времени. В проведенных мной тестах объекты DataSet без контроля ти пов обеспечивали чуть более высокую производительность (на выше), чем объекты DataSet со строгим контролем.

Что еще следует учесть Объекты DataSet со строгим контролем типов упрощают написание кода и позво ляют сохранить душевное спокойствие. В следующих разделах рассказывается, что еще следует учесть, если вы собираетесь использовать объекты DataSet со стро гим контролем типов.

Осуществление структурных изменений Если вы изменяете структуру объекта DataSet со строгим контролем типов, добавляя или удаляя некоторые объекты вам потребуется сгенерировать объект DataSet заново. Помните об этом при создании многоуровневого приложения, промежуточный уровень которого возвращает объекты DataSet со строгим конт ролем типов. После того как вы повторно сгенерируете объект DataSet со стро гим контролем типов, возвращаемый промежуточным уровнем, вам потребуется заново собрать приложение, предварительно обновив ссылку на объект проме ГЛАВА 9 Работа с объектами DataSet со строгим контролем типов уровня. Тем не менее, если вы собираетесь менять структуру данных, возвращаемых сервером, лучше всего изменить обращающийся к этим данным клиентский код, независимо от того, используется ли обычный объект DataSet или объект DataSet со строгим контролем типов.

Преобразование объектов DataSet Объекты DataSet со строгим контролем типов наследуют от стандартного класса DataSet, и поэтому следующий фрагмент кода, обращающийся к объекту DataSet со строгим контролем типов через интерфейс DataSet без контроля не является ошибочным:

Visual Basic Dim As Mew Dim As DataSet = CType(dsStrong, Visual C# dsStrong = DataSet dsUntyped;

dsUntyped = (DataSet) dsStrong;

Однако имейте в виду, что объект DataSet без контроля типов до класса DataSet со строгим контролем типов только если обычный объект DataSet изначально создавался как экземпляр этого самого класса со строгим контролем типов. Следующий фрагмент поможет вам уяснить все непонятные моменты дан ной модели поведения:

Visual Basic Dim dsStrongl, dsStrong2 As Dim dsUntyped As DataSet код выполнится успешно = New dsUntyped = CType(dsStrong1, DataSet) dsStrong2 = CType(dsUntyped, код сгенерирует исключение dsUntyped = New CType(dsUntyped, Chapter9) Visual C# dsStrongl, DataSet dsUntyped;

код успешно dsStrongl = Chapter9();

dsUntyped = (DataSet) dsStrongl;

342 Часть III Автономная работа с данными: объект DataSet модели = код сгенерирует исключение dsUntyped = new = dsUntyped;

А что, если у вас есть объект DataSet без контроля типов, и вы хотите обра щаться к его содержимому с класса DataSet со строгим контро лем типов? Если объект DataSet без контроля типов создавался как экземпляр обычного класса DataSet, преобразовать его в экземпляр класса DataSet со гим контролем типов нельзя. Тем не можно методом Merge класса DataSet со строгим контролем типов импортировать содержимое обычного объекта Visual Basic Dim dsStrong As New Chapter9() Dim dsUntyped As New Visual C# Chapter9 dsStrong = new Chapter9();

DataSet dsUntyped = new dsSt Метод Merge также при обмене данными между экземплярами различ ных классов со строгим контролем типов. Для такого обмена данными разрешается также применять методы и если включить в их XML-схему Возможность генерирования данных без контроля типов что ваше приложение использует объекты DataSet со строгим кон тролем и требуется отсылать их серверу промежуточного уровня для пе редачи изменений в БД. Метод объекта DataSet со строгим контролем типов позволяет создать новый объект DataSet, содержащий только измененные записи. Однако этот метод возвращает объект DataSet без контроля типов. Удаст ся ли обычный объект DataSet, возвращаемый методом в объект DataSet со строгим контролем типов? Безусловно да, что и ют фрагменты:

Visual Basic Dim As New объект DataSet со строгим контролем типов часть его записей Dim dsUntyped As DataSet dsUntyped = Dim As = CType(dsUntyped, ГЛАВА 9 Работа с DataSet со строгим контролем типов Visual dsStrongAllRows = new Chapter9();

объект DataSet со строгим контролен типов изменяем часть его записей DataSet dsUntyped;

= = dsUntyped;

У объекта DataSet со строгим контролем типов есть и другие которые возвращают данные без контроля типов. Так, метод Select возвращает массив объек тов Преобразовать его целиком в массив объектов DataSet со строгим контролем типов нельзя, однако это можно сделать с отдельными объектами DataRow.

То же верно и для объекта Обратиться к его содержимому напрямую через классы со строгим контролем типов но, используя показанный ниже код, удается преобразовать объект DataRow. возвращаемый свойством Row объекта в класс со строгим контролем типов:

Visual Basic Dim dsStrong As New Chapter9() объект DataSet со строгим изменяем часть его Dim As New Dim As rowCustomer = Visual C# dsStrong = new объект DataSet со строгим контролем типов часть его записей DataView vueCustomers = new rowCustomer;

rowCustomer = Выбор способа разработки Так что же подходит вам больше всего? Объекты DataSet со строгим контролем типов ускоряют разработку приложения и упрощают написание эффективного кода. Тем не менее их производительность невысока. Написав грамотный код на основе объектов DataSet без контроля типов, вы создадите приложение с более высокой производительностью.

Все зависит от назначения вашей программы. Если важнее высокая произво дительность, используйте только обычные объекты DataSet. Если же несколько часов, сэкономленных при разработке, сопоставимы со степенью падения про обратите внимание на объекты DataSet со строгим контролем типов, 344 Часть III Автономная работа с данными: объект DataSet модели Вопросы, которые стоит задавать почаще Вопрос. Я хочу обеспечить максимальную производительность своих компонен тов промежуточного уровня и использую на сервере объекты DataSet без контро ля типов. Тем не менее при создании клиентской части приложения очень удоб ны одноименные объекты со строгим контролем типов. Можно ли как-то восполь зоваться преимуществами и тех, и других объектов?

Ответ. Да. Пусть ваши компоненты промежуточного уровня возвращают и при нимают объекты DataSet без контроля типов. В клиентском приложении исполь зуйте экземпляры объектов DataSet со строгим контролем типов и с помощью метода Merge импортируйте содержимое объектов DataSet без контроля типов, возвращаемых промежуточным уровнем.

Вопрос. У объектов DataSet очень мало функций проверки. Я не могу задать свой ства объекта DataSet как со строгим типов, так и без контроля чтобы гарантировать, что значение попадает в определенный интервал. Можно ли добавить в файл класса дополнительный код для расширенной проверки зна чений?

Ответ. Конечно. При необходимости свойства классов со строгим контролем типов можно дополнить кодом проверки, однако эти классы DataSet в общем-то не рассчитаны на такое дополнение. Код проверки не сохранится в.xsd-файле класса DataSet со строгим контролем типов. Если вы измените содержимое.xsd файла, Visual Studio заново сгенерирует класс DataSet со строгим контролем типов и вы потеряете написанный вами код, Есть еще одно решение: создать новый класс, наследующий от класса DataSet со строгим контролем типов, и дополнить первый нужным кодом проверки.

Вопрос. Какие возможности доступны при генерировании класса с помощью утилиты XML Schema Definition Tool?

В разделе Using with a Typed DataSet* справочной системы Framework SDK перечислены способы управления именами классов со строгим контролем типов, генерируемых этой утилитой. Там также описана реакция свойств классов DataSet со строгим контролем типов, содержащих зна чения NULL. Утилита XML Schema Definition просматривает ваш.xsd-файл на предмет аннотаций, перечисленных в документации.

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

Набор объектов и упрощает программ ное добавление аннотаций в DataSet:

ГЛАВА 9 Работа с объектами DataSet со строгим контролем типов Visual Basic Dim As New = Dim As DataTable = Dim col As имя со строгим контролем типов для DataTable имя свойства DataTable объекта DataSet col поле содержит NULL, пусть класс возвращает col = поле содержит значение NULL, пусть класс возвращает col = поле содержит значение NULL, пусть класс возвращает О "С:

Visual C# DataSet ds = new = DataTable tbl = DataColumn col;

имя объекта со строгим контролем типов для объекта DataTable имя свойства DataTable объекта DataSet col = поле содержит значение NULL, пусть класс возвращает col = поле содержит значение NULL, пусть класс col = поле содержит значение NULL, пусть класс возвращает О 1О Передача обновлений в базу данных Веря в недоступные твоему ты Быть Ч неправильный жизни.

Wonder) Ваидер, скорее всего, говорил не о передаче обновлений в БД, при веденная цитата вполне релевантна к теме. предоставляет про граммистам БД беспрецедентно мощную и гибкую подсистему передачи лений. Тем не менее, судя по вопросам, на которые мне приходилось отвечать в различных группах новостей и на конференциях в период тестирования бета версии я могу сказать, что лишь небольшая группа разработчиков действи тельно понимает, как эффективно использовать эти новые возможности.

Большинство встречавшихся мне фрагментов кода на ADO.NET генерируют логику при помощи объекта В фрагментах есть предупреждение о необходимости генерировать собственную логику обнов ления, но практически нигде не объясняется, зачем это нужно и как это сделать.

Сколько раз вы интересовались у разработчиков, как работает их код, и те пожимали улыбались и отвечали: Он просто Именно это суеверие я хочу развеять в этой и следующей главах.

Примечание Я действительно видел службу технической поддержки, девизом которой был лозунг: просто работает. Печально, но это так.

Чем глубже вы понимаете, как передавать обновления с помощью ADO.NET. тем проще генерировать собственную логику обновления и/или передавать об новления с использованием хранимых процедур. Я расскажу, как средствами объек ГЛАВА Передача обновлений в данных та передавать отложенные изменения из объекта в БД, а так же о специальных утилитах, экономящих время и не отрицатель но влияния на производительность и возможности управления.

Если вы последовательно читали материал книги, то уже умеете создавать объек ты DataSet со строгим контролем типов и без такового для хранения данных, возвращаемых объектами Кроме того, вы должны уметь изменять содержимое объектов DataSet. Эта глава познакомит вас с основами передачи изменений из объектов DataSet в БД при помощи объектов DataAdapter.

Рассмотрим заказ из БД На рис. 10-1 показан запрос, выполненный в SQL Server Query Analyzer для получения сведений об этом заказе. Предположим, клиент звонит и хочет изменить заказ. Сыр не продается, но бутылки ост рого соуса прямо-таки разлетаются с и люди постоянно спрашивают чай.

?

D batch Рис. 10-1. Содержимое заказа в БД Northwind Из главы 5 вы знаете, как поместить результаты запроса в объект DataSet. Ис пользуя эти знания, вы без труда создадите приложение, выбирающее заказы кли ентов в объекты DataSet. А на материалах главы 6, сумеете сделать так, чтобы приложение изменяло содержимое объекта DataSet в соответствии с инструкциями пользователя. Но, как я уже говорил, изменение содержимого DataSet не отражается на соответствующих записях БД.

В главе 5 я рассказал о том, что объект DataAdapter предоставляет метод позволяющий передавать в БД отложенные изменения. Таким образом, можно создать приложение, передающее изменения в составе заказа при помощи следу ющего кода:

Visual Basic заказа в объект DataTable Dim As String = & _ "Initial strSQL = "SELECT Quantity, & _ [Order Details] OrderlD = 10503 & _ "ORDER BY ProductID" Dim da New strConn) Dim As New 348 Часть III Автономная работа с данными: объект DataSet модели ADO.NET заказа = * 1, 24, отложенные изменения Try submitted new changes") Catch ex As Exception to & _ "threw & & End Visual C# содержимое заказа в string strConn = + "Initial = "SELECT Quantity, + "FROM [Order Details] WHERE OrderlD = 10503 + "ORDER BY ProductID";

OleDbDataAdapter da = new strConn);

DataTable = new содержимое заказа (short) - 2;

object[] 1, 24, изменения try submitted new catch (Exception ex) ;

to threw + Данный код успешно компилируется, но не способен успешно передать в БД изменения в составе заказа. Вместо изменений ADO.NET сгенерирует ис гласящее: Update requires a valid when passed DataRow collection with deleted rows* (Методу Update, принявшему набор DataRow с уда записями, необходим корректный объект ГЛАВА 10 Передача обновлений в базу данных На этапе бета-версии Microsoft Framework такие исключе ния путали многих разработчиков. Предыдущие технологии доступа к данным, например включали функции, позволявшие автоматически передавать из менения. В ADO.NET передавать изменения разрешается средствами объекта Data Adapter, однако по умолчанию он не содержит необходимой для этого логики.

Так как же добавить в объект ADO.NET логику передачи измене ний? Есть три способа: написать собственный код, указать ADO.NET сгенериро вать логику обновления за вас или воспользоваться утилитой генерирования кода, например мастером Data Adapter Configuration Wizard из состава Visual Studio Я сейчас познакомлю вас с сутью, а также с преимуществами и недостатками этих трех способов.

Урок истории чем обсуждать передачу обновлений средствами ADO.NET, рассмотрим, как она осуществлялась в технологии, предшествовавшей ADO.NET, Ч ADO. В от личие от ADO.NET, ADO автоматически генерирует логику обновления. Я вкратце расскажу, как ядро курсоров ADO передает изменения, чтобы вы поняли, как и почему команда ADO.NET выбрала другой путь и подталкивает про граммистов к написанию собственной логики обновления. Зная, как ядро курсо ров ADO передает изменения, вам будет проще понять, как генерировать собствен ную логику обновления в ADO.NET.

Ядро курсоров ADO поддерживает функциональность, аналогичную возмож объекта ADO.NET. Клиентский объект Recordset ADO можно исполь зовать в качестве автономного кэша данных. Кроме объект Recordset Ч это механизм ADO для передачи обновлений в БД.

Следующий фрагмент кода выбирает содержимое обсуждавшегося выше зака за, изменяет это содержимое и затем передает отложенные изменения в БД:

Классический Visual Basic и ADO 2.x Dim As String, As = & _ "Initial strSQL = "SELECT & _ "FROM [Order Details] = & _ BY ProductID" Dim As Set = New = adUseClient strSQL, strConn, adOpenStatic, adCmdText * = 2 350 Часть Автономная работа с данными: объект DataSet модели = = Данный код демонстрирует большинство преимуществ и недостатков переда чи обновлений в БД с объекта Recordset ADO, и я подробно рас скажу об этом в следующих разделах этой главы.

Преимущества передачи обновлений с использованием объектов Recordset ADO Первое преимущество данного подхода Ч минимальный объем необходимого кода.

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

Код не содержит логики обновления, поскольку ADO автоматически генери рует ее в период выполнения. Это Ч второе преимущество. ADO не требует от вас программно предоставить логику Фактически ее может написать человек, обладающий минимальными знаниями языка SQL: для использования функций обновления, ядром курсоров ADO, не требуется пони мать, что такое параллелизм, блокировки, и как сгенерировать SQL-запрос UPDATE.

Тот факт, что разработчикам удается создавать работающие приложения для ступа к данным, не зная основ языка SQL, Ч отличное подтверждение продуман ности архитектуры технологии ADO. Меня постоянно поражает (в хорошем смыс ле), что многие разработчики передают обновления при помощи ядра курсоров ADO и совершенно не представляют, как именно ядро выполняет эту работу, Недостатки передачи обновлений с использованием объектов Recordset ADO К сожалению, у функций предоставляемых ядром курсоров ADO, есть и недостатки Ч низкая производительность и ограниченные возможности управ ления. С момента выхода ADO 1.5 бесчисленное множество разработчиков пере давало обновления в БД средствами ядра курсоров ADO, так что эти проблемы решаемы. Тем не менее они достаточно велики. Чтобы глубже понять указанные недостатки, посмотрим, как ядро курсоров ADO передает изменения в БД.

При вызове метода ядро курсоров ADO сканирует объект Recordset на наличие измененных записей и преобразует изменения отдельных записей в SQL-запрос, редактирующий соответствующую запись БД. Ранее я го ворил о разработчиках, создающих собственные SQL-запросы UPDATE, INSERT и ГЛАВА 10 Передача в базу данных DELETE для изменения содержимого БД. Ядро курсоров ADO анало гичные операторы.

Для наблюдения за к БД годится SQL Profiler.

запросы, генерируемые ядром курсоров ADO для передачи изменений в БД, увидите вызов хранимой SQL Server с пакетом парамет ризованных запросов. Этот вызов эквивалентен следующим запросам:

DELETE [Order Details] WHERE = 10503 AND = UPDATE [Order Details] SET Quantity = WHERE OrderlD = 10503 ProductID = 65 AND Quantity = INSERT INTO [Order Details] (OrderlD, ProductID, Quantity, VALUES (10503, 1, 24, 18) После повторного исходный запрос и программно вносимые в содержимое объекта Recordset, должны стать вам понятны, т. е. вы сможете посмот реть запросы и понять их назначение, даже если не умеете создавать их само стоятельно. Преобразовывать изменения содержимого Recordset в SQL-запросы очень просто, если вам известно происхождение данных.

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

Для просмотра этих данных самостоятельно достаточно воспользоваться в коде набором Properties объекта Field ADO:

With = & = & = & End With Здесь проявляется первый значительный недостаток функций обновления, предоставляемых ядром курсоров ADO, производительность. Запросы, генери руемые ядром для получения из БД сведений о таблицах, столбцах и первичных ключах, сильно снижают производительность. созда ющих код для доступа к данным, известно происхождение этих данных. К сожа лению, в ADO нельзя предоставить такие данные в коде и исключить необходи мость их из БД каждый раз при открытии объекта Recordset.

Ядро курсоров ADO выполнено по технологии черного ящика* и не позволя ет программистам определять собственную логику обновления. Это второй зна чительный недостаток функций обновления, предоставляемых ядром ADO. И хотя логика обновления ядра курсоров ADO весьма впечатляюща, управ лять ей практически невозможно. Также нельзя передавать в объекте Recordset изменения посредством вызовов хранимых процедур. Если вам не логика обновления, генерируемая ядром ADO, вы полностью доставлены сами себе.

352 Часть работа с данными: объект DataSet модели Передача обновлений с помощью объектов Command модели Как вы уже ядро ADO создает для передачи обновлений в БД параметризованные запросы. Используя материал главы 4, можно создавать эк вивалентные параметризованные запросы на В следующих главы рассказывается, как с помощью этих параметризованных объектов Command пе редавать в БД изменения из DataSet ADO.NET.

Наши объекты Command ADO.NET будут не столь динамическими, как их ADO аналоги. Чтобы упростить задачу, создадим один объект обработки обновлений, один Ч для вставок и один Ч для удалений. Они основаны на следу ющих параметризованных запросах:

UPDATE [Order SET = ?, = ?, Quantity = ?, UnitPrice = ?

WHERE OrderlD = ? AND ProductID = ? AND Quantity ? AND UnitPrice = ?

INTO [Order (OrderlD, ProductID, Quantity, UnitPrice) VALUES (?. ?, ?, ?) DELETE [Order WHERE OrderlD = ? AND ProductID = ? AND Quantity = ? AND UnitPrice = ?

Примечание Запросы UPDATE и INSERT передают в БД новые значения всех полей оригинального поскольку те указаны в их разделах WHERE.

У данного способа есть как преимущества, так и недостатки, которые мы подробно обсудим позже.

Следующий фрагмент кода создает три параметризованных объекта Command.

В каждом случае код предполагает, что имеется внешне определенный объект с именем си.

Visual Basic Private Function As Dim strSQL As String = "UPDATE [Order Details] & _ SET OrderlD ?, ProductID = ?, & _ Quantity = ?, UnitPrice = ? & _ WHERE OrderlD = ? AND ProductID = ? AND Quantity ? AND UnitPrice = Dim As New Dim pc As = ГЛАВА Передача обновлений в базу данных Return cmd End Function Private Function As OleDbCommand Dim As String = "INSERT INTO [Order Details] & _ VALUES ?, ?, Dim cmd As New Dim pc As OleDbParameterCollection = Return cmd End Function Private Function As OleDbCommand Dim strSQL As String strSQL = "DELETE FROM [Order Details] & _ WHERE OrderlD = ? AND ProductID = ? AND ' Quantity = ? AND UnitPrice = Dim cmd As New Dim pc As OleDbParameterCollection = Return cmd End Function Visual C# static OleDbCommand { string strSQL;

strSQL = "UPDATE [Order Details] & _ SET OrderlD = ?, ProductID = ?, + Quantity = ?, UnitPrice - ? " + 354 Часть III Автономная работа с данными: объект DataSet модели WHERE = ? AND = ? AND + Quantity = ? AND = = new OleDbParameterCollection pc = return cmd;

i static OleDbCommand string = "INSERT INTO (OrderlD, ProductID, Quantity, UnitPrtce) + VALUES (?, ?, ?, OleDbCommand cmd = new OleDbParameterCollection pc = return static OleDbCommand string strSQL;

StrSQL = "DELETE FROM [Order Details] + WHERE OrderlD = ? AND ProductID = ? AND Quantity ? AND UnitPrice OleDbCommand cmd = new OleDbParameterCollection pc = return cmd;

Х Передача в базу данных Передавать обновления с помощью наших параметризованных объектов Com mand очень просто. Для начала просмотрим измененные записи в объекте и определим, как именно они изменены (обновление, вставка или удаление). За тем на основе содержимого записи подставим значения параметров в соответству ющий запрос.

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

Visual Basic Private Sub Dim As = Dim As = Dim cmdDelete As OleDbCommand = Dim As Dim As Integer Dim dvrs As dvrs = Or Each row In Select Case = cmdUpdate) Case intRowsAffected = cmdlnsert) Case intRowsAffected = cmdDelete) End Select If intRowsAffected = Else = "Update attempt failed" End If Next End Sub Private Function row As _ As OleDbCommand) As Integer Dim pc As OleDbParameterCollection = = = = = = _ = _ 356 Часть III Автономная работа с данными: объект DataSet модели = _ = Return End Function Private Function row As _ As As Integer Dim pc As OleDbParameterCollection = = = = Return End Function Private Function As ByVal cmd As As Dim pc As OleDbParameterCollection = = = = = Return End Function Visual C# static void OleDbCommand cmdUpdate = OleDbCommand = OleDbCommand cmdDelete = DataViewRowState dvrs;

dvrs = | | int = 0;

foreach row in { switch !

case intRowsAffected = cmdUpdate);

break;

case intRowsAffected = cmdlnsert);

break;

case intRowsAffected = cmdDelete);

ГЛАВА Передача обновлений в базу данных break;

} if == 1) = "Update attempt failed";

static row, cmd) { pc = = = = = = = = return static int SubmitInsert(DataRow OleDbCommand { OleDbParameterCollection pc = = = = = return cmd.

static int row, OleDbCommand cmd) { OleDbParameterCollection pc = = DataRowVe = = return cmd. ExecuteNonQueryO;

358 Часть Автономная работа с данными: объект DataSet модели Примечание Предыдущий кода просматривает измененные записи с метода У меня была очень веская причина не использовать циклы For или For Each для просмотра всех элементов набора объекта DataTable. После того как вы успешно передали отложенное удаление и вызвали соответствующего объекта запись удаляется из набора родительского объекта.

Метод возвращает массив объектов DataRow, по сути, на измененные записи. Если удалять элементы из набора Rows объекта DataTable, код по-прежнему будет успешно выполняться.

Теперь пора объединить весь показанный код.

Следующий фрагмент выбирает заказанные товары в объект изме няет содержимое заказа и передает изменения Б БД. Код демонстрирует, что все предыдущие фрагменты успешно передают отложенные изменения, и использу ет определенные выше процедуры. Кроме того, он включает процедуру, которая выводит текущее содержимое объекта DataTable и позволяет убедиться в успеш ности обновления состава заказа. Код также содержит процедуру ко торая восстанавливает оригинальный состав заказа и позволяет выполнять дан ный фрагмент кода многократно.

Visual Basic Dim As Dim da As OleDbDataAdapter Dim As DataTable = Sub Dim As String strConn = 4 _ "Initial strSQL = "SELECT ProductID, Quantity, & "FROM [Order Details] WHERE OrderlD = 10503 & _ "ORDER BY ProductID" = New da = New contents of database") data DataSet") contents of database") End Sub ГЛАВА 10 Передача обновлений в базу данных Private Sub ModifyOrderO Dim row As = = = Int16) - = = = = = 18. End Sub Public Sub strStatus As String) Dim As Dim col As DataColumn ProductID & "Quantity For Each In "ProductID") Each col In & & vbTab) Next Next End Sub Private Sub Dim As String Dim cmd As = StrSQL = "DELETE [Order Details] WHERE OrderlD = 10503" = strSQL cmd. ExecuteNonQueryO StrSQL = "INSERT INTO [Order Details] & _ (OrderlD, ProductID, Quantity, UnitPrice) & _ VALUES (10503, 14, 70, 23.25) = strSQL cmd. ExecuteNonQueryO strSQL = "INSERT INTO [Order Details] & _ (OrderlD, ProductID, Quantity, UnitPrice) & _ VALUES (10503, 65, 20, 21.05)" = strSQL cmd. ExecuteNonQueryO End Sub 360 Часть Автономная работа с данными: объект DataSei модели Public Function As DataTable Dim As New Details") Dim col As With col = col.AllowDBNull = False col = col.AllowDBNull = False col = col.AllowDBNull False col = col.AllowDBNull = False End With = New Return tbl End Function Visual C# static OleDbConnection static da;

static DataTable tbl;

static void args) { string = + "Initial strSQL = "SELECT Quantity, + "FROM [Order Details] WHERE OrderlD = 10503 + "ORDER 8Y ProductID";

= new da = new tbl = contents of contents of contents of static void ModifyOrderO ГЛАВА 10 Передача обновлений в базу данных = = = (Int16) = 10503;

= 1;

= 24;

= 18.0;

static void { +. "Quantity row in { col in + row[col] + Console. WriteLineO;

static void < string cmd = strSQL = "DELETE FROM [Order Details] WHERE = strSOL = "INSERT INTO [Order Details] + ProductID, Quantity, UnitPrice) VALUES 14, 70, 23.25) = strSQL;

StrSQL = "INSERT INTO [Order Details] + (OrderlD, ProductID, Quantity, UnitPrice) VALUES (10503, 65, 20, = strSQL;

static DataTable 362 Часть Автономная работа с данными: объект DataSet модели DataTable = new col;

col = = false;

col = col.AllowDBNull false;

col = = false;

col = = false;

new return tbl;

!

Мы только что написали большой объем кода для передачи отложенных из менений. Код, на основе которого генерируются параметризованные объекты Command, уникален для исходного запроса. Тем не менее код процедуры Он просматривает в объек те DataTable, определяет, как именно объекты DataRow, фун кцию, запрос для передачи отложенного изменения, и затем, в зависимости от возвращенного соответствующим образом помечает объект DataRow.

По существу, мы воссоздали функциональность обновления, предоставляемую объектом DataAdapter. Подробнее о нем Ч ниже.

Передача обновлений с использованием объектов ADO.NET В главе 5 рассказывалось об использовании объекта для записи ре зультатов запросов в объекты DataTable. но это лишь половина функциональнос ти Данный объект также предназначен для передачи отложенных изменений из объектов DataSet, Чтобы создать логику используемую объектами DataAdapter для передачи изменений в БД, можно:

Х вручную программно сконфигурировать объекты Х воспользоваться в период выполнения объектом Х воспользоваться в период разработки мастером Data Adapter Configuration Wizard.

У каждого из этих способов есть свои преимущества и недостатки, которые я подробно разберу далее.

Конфигурирование объектов DataAdapter вручную DataAdapter предоставляет четыре содержащих объекты Как вы помните, свойство содержит объект Command, при помо щи которого DataAdapter заполняет ваш объект DataTable. Три остальных ГЛАВА Передача обновлений в базу данных и объекты Com при помощи которых DataAdapter передает изменения.

архитектура сильно отличается от объектной модели ADO. Волшебная технология больше не используется. Вы управляете тем, как Data Adapter передает отложенные поскольку используемые им объекты Command.

Метод Update объекта DataAdapter очень гибок и принимает объект и имя таблицы, объект или массив объектов симо от того, как вызван метод DataAdapter попытается дать изменения при помощи соответствующего объекта Command.

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

Для передачи отложенных изменений с приме няются такие же параметризованные запросы.

UPDATE [Order Details] SET = ?, = ?, Quantity = ?, = ?' WHERE OrderlD = ? AND ProductID = ? AND Quantity = ? AND UnitPrice = ?

INSERT INTO [Order Details] (OrderlD, ProductID, Quantity, UnitPrice) VALUES (?, ?, ?, ?) DELETE [Order Details] WHERE OrderlD = ? AND ProductID = ? AND Quantity = ? AND UnitPrice = ?

Тем не менее при добавлении параметров в объекты Command, хранящиеся в свойствах объекта мы используем два свойства объекта Parameter предназначенные специально для обновлений на основе и SourceVersion.

По сути, эти свойства связывают объект Parameter с объектом из состава Перед выполнением запроса DataAdapter на основе данных свойств определяет, какое значение задать Value объекта Parameter, ана логично тому, как это осуществлялось в функциях Submitlnsert и Подробнее Ч на рис. 10-2.

Следующий фрагмент кода не только создает параметризованные объекты Command, но и задает значения свойств SourceColumn SourceVersion объектов Parameter. Значение свойства по умолчанию SourceVersion Ч и задавать его только если объект Parameter требуется связать с ориги нальными значениями нужного столбца.

364 Часть III Автономная работа с данными: объект DataSet модели OrderlD 10503 65 20 21. (текущая версия) UPDATE [Order Details] SET = ?, ProductiD = ?, Quantity = ?, = ?

WHERE = ? AND ProductiD = ? AND = ? AND UnitPrice = ?

10503 65 40 21. версия) Quantity UnitPrice Рис. 10-2. Связывание объектов с объектами Visual Basic Function As Dim As String = "UPDATE [Order Details] " & _ SET OrderlD = ?, ProductiD = ?, " & _ Quantity = ?, UnitPrice = ? & _ WHERE OrderlD = ? AND ProductiD = ? AND " & _ Quantity = ? AND UnitPrice = Dim As New Dim pc As = 0, 0, 0, "Quantity") 0, Dim As = 0, "OrderlD") = param = 0, = param = 0, "Quantity") = param = 0, "UnitPrice") = Return cmd End Function Private Function As OleDbCommand ГЛАВА 10 Передача обновлений в базу данных Dim strSQL As String strSQL = "INSERT INTO [Order Details] & _ Quantity, & VALUES (?, ?, ?, Dim As New Dim pc As = 0, "OrderlD") 0, "ProductID") 0, 0, "UnitPrice") Return cmd End Function Private CreateDataAdapterDeleteCommand() As OleDbCommand Dim strSQL As String StrSQL = "DELETE FROM [Order Details] & _ WHERE OrderlD = ? AND ProductID = ? AND & _ Quantity = ? AND UnitPrice = Dim cmd As New Dim pc As OleDbParameterCollection = Dim As 0, "OrderlD") = 0, "ProductID") = 0, "Quantity") = 0, = Return cmd End Function Visual static OleDbCommand { string strSQL;

StrSQL = "UPDATE [Order Details] & _ SET OrderlD = ?, ProductID = ?, + Quantity = ?, UnitPrice = ? + WHERE OrderlD = ? = ? AND + Quantity = ? AND UnitPrice = OleDbCommand cmd = new OleDbParameterCollection pc = 0, 366 Часть Автономная работа с данными: DataSet модели 0, 0, 0, = 0, = param = 0, = param = 0, = param = 0, = return cmd;

static i string = "INSERT INTO [Order Details] + (OrderlD, ProductID, Quantity, UnitPrice) + VALUES {?, ?, ?, ?)";

OleDbCommand cmd new pc = 0, 0, 0, 0, return cmd;

i static OleDbCommand { string strSQL;

strSQL = "DELETE FROM Details] WHERE OrderlD = ? AND ProductID = ? AND + = ? AND UnitPrice = OleDbCommand cmd = new 01eDbCommand(strSQL, OleDbParameter param;

OleDbParameterCollection pc = param = pc.Add( "OrderlD", 0, = param = 0, ГЛАВА 10 Передача обновлений в базу данных = = 0, = param = 0, = return } Теперь процедуры и можно следующим кодом;

Visual Basic Private Sub = = = End Sub C# static void { = = = Передача обновлений с использованием хранимых процедур Одна из наиболее жалоб разработчиков, получавших данные БД сред ствами ADO, Ч невозможность использования метода для передачи обновлений при помощи хранимых процедур.

Как уже говорилось, позволяет вам определить собственную ло гику обновления. В приведенных ранее фрагментах кода показано, как создать собственные объекты Command, с помощью которых DataAdapter будет переда вать отложенные изменения в БД. Аналогичный код годится и для передачи об новлений средствами хранимых Во-первых, в БД следует определить хранимые процедуры, позво ляющие изменять, вставлять и удалять записи таблицы Order Details. Для созда ния процедур, которые будут в нашем скопируйте и выполните следующий фрагмент кода в SQL Server Query Analyzer. Если у вас установлено только ядро и, как следствие, нет доступа к SQL Server Query процедуру (приводится в одном из последующих фрагментов кода) и создайте нужные хранимые 368 Часть Автономная работа с данными: объект DataSet модели USE GO CREATE PROCEDURE spUpdateDetail int, int, money, int, smallint, AS UPDATE [Order Details] SET = = Quantity = UnitPrice = WHERE OrderlD = AND ProductID = AND Quantity = AND UnitPrice = GO CREATE int, int, smallint, money) AS INSERT INTO Details] (OrderlD, ProductID, UnitPrice) VALUES GO CREATE PROCEDURE spDeleteDetail int, int, smallint, AS DELETE FROM [Order Details] WHERE OrderlD = AND ProductID = AND Quantity AND UnitPrice = Имея хранимые процедуры для обновлений в таблицу Order Details, можно написать объекты автоматически вызывающие их при вызове метода Следующий фрагмент кода включает функции, которые создают объекты содержащие вызовы приведенных выше процедур. Кроме того, он ет процедуру для создания всех этих хранимых процедур в БД. Все, что остается сделать для передачи обновлений с помощью хранимых процедур Ч связать наши новые объекты объектом Это осуществляется в процеду ре Visual Basic Private Sub = = ГЛАВА Передача обновлений в данных = End Sub Private Function As Dim cmd As New = Dim pc As = 0, 0, Dim As param = 0, "OrderlD") = param = 0, "ProductID") = param = 0, "Quantity") = param = 0.

= Return cmd End Function Private Function As OleDbCommand Dim cmd As New = Dim pc As OleDbParameterCollection = 0, "OrderlD") 0, "ProductID") 0, "Quantity") 0, Return cmd End Function Private Function As OleDbCommand Dim cmd As New = Dim pc As OleDbParameterCollection = Dim param As OleDbParameter param = 0, "OrderlD") 370 Часть III Автономная работа с данными: объект DataSet модели = param = 0, "ProductlD") = param = 0, "Quantity") = param = 0, Return cmd End Function Private Sub Dim cmd As = Dim As String = "CREATE PROCEDURE spUpdateDetail & vbCrLf & _ int, & vbCrLf & _ smallint, & vbCrLf & money, & vbCrLf & int, & vbCrLf & _ int, & vbCrLf & smallint, & vbCrLf & money) & vbCrLf & "AS & vbCrLf & _ "UPDATE [Order Details] & vbCrLf & _ SET = & vbCrLf & _ ProductlD = & vbCrLf & _ Quantity = & vbCrLf & _ UnitPrice = & vbCrLf & WHERE OrderlD = AND & vbCrLf ProductlD = AND & vbCrLf & Quantity = AND & vbCrLf UnitPrice = = strSQL strSQL "CREATE PROCEDURE spInsertDetail & vbCrLf & _ int, & vbCrLf & _ smallint, money) & vbCrLf & "AS & vbCrLf & _ "INSERT INTO [Order Details] & vbCrLf & _ (OrderlD, ProductlD, Quantity, & vbCrLf & _ VALUES strSQL ExecuteNonQueryO strSQL = "CREATE PROCEDURE spDeleteDetail & vbCrLf & _ int, int, " & vbCrLf & smallint, & vbCrLf & "AS & & ГЛАВА 10 Передача обновлений в базу данных "DELETE [Order Details] & & _ = AND & & _ ProductID = AND & vbCrLf & Quantity = AND = End Sub Visual C# static void { = CreateUpdateViaSPCommandO;

= = static CreateUpdateViaSPCommandO { OleDbCommand cmd = new pc = 0, 0, 0, 0, OleDbParameter param;

= 0, = param = 0, = param = 0, param = 0, = return cmd;

static OleDbCommand i OleDbCommand cmd = new = pc = 0, 0, 372 Часть III Автономная работа с данными: объект DataSet модели 0, 0, return ) static { cmd = new = pc = param;

0, = param = 0, = param = OleDbType.Smalllnt, 0, = param = 0, = return cmd;

} static void { OleDbCommand cmd = string strSQL;

strSQL = "CREAJE PROCEDURE + int, int, + money, + int, int, + smallint, money) + "AS + "UPDATE [Order Details] + SET = + ProductID = + Quantity = + UnitPrice = + WHERE OrderlD = \n\r" + ProductID = AND \n\r" + Quantity = AND + UnitPrice = = strSQL;

strSQL = "CREATE PROCEDURE spInsertDetail + int, int, + ГЛАВА Передача обновлений базу данных money) + "AS + "INSERT INTO [Order Details] + (OrderlD, Quantity, + VALUES = strSQL = "CREATE PROCEDURE spDeleteDetail int, money) \n\r" "AS + [Order Details] + OrderlD AND + ProductID = AND + Quantity = AND UnitPrice = strSOL;

Использование собственной логики обновления Рассмотрим преимущества и недостатки применения логики обнов ления в коде.

Преимущества Два важнейших преимущества использования собственной логики обновления в коде Ч расширенные возможности управления и производительность. По срав нению с предыдущими технологиями доступа к данным Microsoft, объект Data Adapter предоставляет самые широкие возможности управления. Исчез ло требование передавать обновления непосредственно в таблицы;

наконец-то стало возможно быстро и эффективно использовать хранимые процедуры.

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

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

374 Часть III Автономная работа с данными: объект DataSet модели Недостатки Недостатки использования логики обновления в коде Ч зеркальное отражение преимуществ ядра ADO. собственная логика новления больше места. назад и сколько кода требуется для передачи обновлений с использованием объекта DataAdapter ADO.NET и сколько Ч для передачи с ядра курсоров ADO. Написание та кого кода Ч много Еще один недостаток в том, что при создании собственной логики обновле ния многие разработчики неуверенно. Им хотелось бы не зада вать вопросов типа Нужно ли взять имя таблицы в запросе в символы-раздели Какие маркеры параметров Какие столбцы использовать в разделе WHERE запросов UPDATE и наиболее подходящее значение свойства для параметра, содержащего значение К счастью, если более быстрые способы логики обновления. Подробнее о них Ч далее в этой главе.

Создание логики обновления с помощью объекта Объектная модель ADO.NET вам не только определить собственную логику но и предоставляет средства динамической генерации такой логики с использованием объекта по аналогии с ядром курсо ров ADO. Если создать экземпляр CommandBuilder и сопоставить его с CommandBuilder попытается сгенерировать логику обновления на основе запроса, хранящегося в свойстве объекта DataAdapter.

Чтобы продемонстрировать принципы работы объекта CommandBuilder, я с его помощью сгенерирую логику обновления для кода, выполняющего запросы к таб лице Order Следующий фрагмент создает экземпляр объекта передавая в конструкторе объект Затем он выво дит запрос, хранящийся в свойстве и сгенерированный объектом CommandBuilder для передачи новых записей.

Visual Basic Dim As strConn = & _ strSQL = "SELECT Quantity, & _ "FROM [Order Details] WHERE OrderlD = 10503 " _ "ORDER BY ProductlD" Dim da As New Dim cb As New Visual C# string strConn, strSQL;

strConn = + ГЛАВА 10 Передача обновлений в данных "SELECT + "FROM [Order WHERE OrderlD = 10503 + "ORDER BY ProductID";

OleDbDataAdapter da = new 01eDbDataAdapter(strSQL, cb = new Как видно, текст запроса довольно сильно походит на запросы, создававшие ся нами в предыдущих разделах главы для передачи новых записей:

INSERT INTO Order Details( OrderlD, ProductID, Quantity, UnitPrice ) Как объект генерирует логику обновления Логика, на основе которой CommandBuilder генерирует запросы UPDATE, INSERT и DELETE, не представляет собой ничего сложного. Как и ядро курсоров ADO.

CommandBuilder обращается к БД за именами базовой таблицы и столбцов, а так же за сведениями о ключевых столбцах набора результатов запроса. Объект Com mandBuilder сгенерирует логику если выполняются все следующие условия:

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

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

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

уже говорилось, первичный ключ что CommandBuilder не более одной записи. Почему объект CommandBuilder налагает ограниче ние на число таблиц, упомянутых в результатах запроса? Подробнее об этом Ч в следующих разделах главы.

выбирает метаданные, необходимые для генерации логики с использованием свойства объекта На самом деле об этом уже говорилось вкратце в главе Метод объек та Command позволяет получить эти метаданные вместе с результатами запроса, как показано ниже:

Visual Basic strConn, strSQL As String StrConn = Data & "Initial strSQL = "SELECT OrderlD, ProductID, Quantity, UnitPrice & _ "FROM [Order Details] WHERE OrderlD = 10503 & _ "ORDER BY ProductID" Dim As New cmd As New Dim As = Or _ Dim As = 376 Часть III Автономная работа с данными: объект DataSet модели Dim row As col As For Each row In tbl.Rows Each col In & & Next col row Visual C# string strConn, = + "Initial strSQL = "SELECT OrderlD, Quantity, [Order Details] WHERE OrderlD = 10503 + BY ProductlD";

OleDbConnection = new = new OleDbDataReader rdr;

= | DataTable tbl = cn.CloseO;

(DataRow in tbl.Rows) { foreach col in + + ' этот код, вы увидите все данные, которыми объект должен обладать о каждом столбце, чтобы сгенерировать логику обновления.

Имя столбца? Имена базовой таблицы и базового столбца для данного столбца?

Является ли столбец частью ключа базовой таблицы? Содержит ли столбец большой объем текстовых или двоичных данных? И т.д., и т.п.

Преимущества и недостатки использования объекта Сравнив фрагмент кода, сгенерированный объектом CommandBuilder, и код, на основе которого мы создавали собственную логику обновления, вы выявите два основных преимущества использования объекта CommandBuilder. Во-первых, требуется меньше кода. Во-вторых, с помощью CommandBuilder удается создавать ГЛАВА 10 Передача обновлений в базу данных логику обновления, имея даже поверхностное представление о SQL-синтаксисе запросов UPDATE, DELETE и INSERT.

Кроме объект если у вас возникли проблемы с собственной логики обновления. Если CommandBuilder успешно сге нерирует необходимую логику, просмотрите значение свойства созданных им объектов Command или значения свойств созданных им объектов Parameter.

Объект CommandBuilder также весьма полезен в приложениях, которым тре буется поддержка обновления данных и в которых вы не хотите просматривать структуру период разработки.

Как и ядро курсоров ADO, объект CommandBuilder автоматически генерирует для вас логику обновления в период выполнения. Таким образом, он подвержен тем же проблемам и ограничениям, что и ядро курсоров ADO.

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

Нельзя передавать обновления средствами хранимых процедур.

Если бы только существовал легкий и быстрый способ генерировать логику обновления в период Создание логики обновления средствами мастера Data Adapter Configuration Wizard В главе 5 рассказывалось, как с помощью мастера Data Adapter Configuration создавать объекты при работе с поставщиками OLE DB и SQL Client Data Provider. Кроме мастер генерирует логику обновления и сохраня ет се в коде.

Одно из назначений мастера Data Adapter Configuration Wizard Ч сгенериро вать в период разработки логику обновления, упростив и ускорив вам создание эффективного кода для обновления данных. Ясно, что это глобальная цель. И мастер защищен от неосторожного обращения (что такое в большин стве ситуаций он действительно создает такой код.

Откройте в Visual Studio проект с элементом, предоставляющим конст руктор (например Web-форму. Web-сервис или и добавьте в конструктор объект Определите строку подключе ния к своей любимой БД и на вкладке SQL statement мастера введите такой запрос:

SELECT Quantity FROM [Order Details] WHERE OrderlD = ? ORDER BY ProductlD Щелкните Next. Откроется окно, аналогичное показанному на рис. 10-3 378 Часть Ml работа с данными: объект DataSet модели wizard has Finish or to make Рис. 10-3. Окно View Wizard Results мастера Adapter Configuration Wizard Просмотр структуры объекта DataAdapter Мастер сгенерировал для нового объекта DataAdapter запросы UPDATE, INSERT и DELETE (рис. 10-3). Щелкните кнопку Finish мастера. Выберите в панели компо нентов новый объект DataAdapter. Затем в окне Properties найдите свойство Delete этого объекта. Выберите свойство и щелкните кнопку справа от его значения. Откроется окно Query Builder с объектом (рис. 10-4).

[Order Рис. 10-4. Объект DeleteCommand, сгенерированный мастером Как видно, запрос, сгенерированный мастером Data Adapter Configuration Wizard для передачи отложенных удалений, идентичен запросу, созданному нами вруч ную в одном из предыдущих разделов главы. Кроме того, можно найти в окне Properties свойства и объекта DataAdapter и про смотреть прочую сгенерированную мастером логику обновления.

ГЛАВА Передача обновлений в базу данных Параметры генерации логики обновления В окне SQL Statement мастера есть кнопка Advanced Options, щелкнув которую вы откроете диалоговое окно, аналогичное показанному на рис. Это окно пре доставляет ограниченные возможности управления логикой обновления, которую генерирует мастер Data Adapter Configuration Wizard.

Если ваш объект только данные из БД, вы сэкономите время на разработку и выполнение, сняв флажок Generate Update And Delete Statements.

По умолчанию мастер Data Adapter Configuration добавляет в раздел WHERE запросов, передающих отложенные изменения и удаления, все столбцы, не содержащие Если снять флажок Use Optimistic Concurrency, ма стер добавит в раздел WHERE таких запросов только поля первичного ключа.

SQL and Delete statements Co dsta Х V Update, and Delete based on statement.

Modifies Update statements to detect whether the has since loaded the helps the DataSet a statement Insert and Update to values, values, and other by the Рис. 10-5. Параметры генерации логики обновления Некоторые БД, например SQL поддерживают пакетные запросы, возвра щающие записи данных. Если вы с помощью мастера Data Adapter Configuration Wizard создаете объект взаимодействующий с такой станет дос тупным и будет помечен флажок Refresh The DataSet. При этом мастер генериру ет запросы, повторно выбирающие содержимое измененной записи сразу после передачи соответствующих изменений. Это означает, что после вызова метода в объекте DataSet появятся новые сгенерированные значения, например значения типа и значения автоинкремента.

Подробнее об этом Ч в следующей главе. Кроме того, мы рассмотрим реали зацию аналогичной функциональности для БД, которые не поддерживают пакет ные возвращающие результаты.

Передача обновлений с помощью хранимых процедур Мастер Data Adapter Configuration Wizard также позволяет создавать объекты DataAdapter, передающие изменения в БД SQL Server посредством хранимых про цедур. В окне Choose A Query Type мастера поставьте переключатель в положение Use Existing Stored Procedures (рис. 10-6) и затем щелкните Next.

380 Часть Ml Автономная работа с данными: объект DataSet модели Нои the date '' SQL a the ID g update Рис. 10-6. Окно Choose A Query Type мастера Data Adapter Configuration Wizard В открывшемся окне можно выбрать для всех объектов Command объекта DataAdapter хранимые процедуры. Сначала задайте значение свойства Доступные процедуры перечислены в раскрывающемся списке (рис.

При выборе процедуры в списке справа отображается возвращаемый ею столбец.

ftdaplei Commands Existing Stored tfie riotedprasziU'sscacJenLJscedVaiY'eoj'' Рис. 10-7. Задание значения свойства SelectCommand объекта DataAdapter Задав значение свойства SelectCommand, определите значения свойств Command и объекта DataAdapter. Чтобы задать значение свойства параметров хранимых процедур, воспользуй тесь списком в правой части окна (рис. 10-8).

Примечание Мастер Data Adapter Configuration Wizard не позволяет задавать значение свойства объектов Parameter. Поскольку по умол чанию оно равно вам следует с помощью окна Properties изме нить его для всех которые вы хотите связать с оригиналь ным значением измененных столбцов.

ГЛАВА 10 Передача обновлений в базу данных Bind to a data I Рис. 10-8. Значения свойства параметров объекта Если у вас установлена Studio Enterprise Edition, можно также зать и мастер Data Adapter Configuration Wizard сгенерирует новые хранимые процедуры SQL Server для свойств InsertCommand и вашего объекта В окне Choose A Query Туре мастера поставьте переключатель в положение Create New Stored Procedures;

мастер предложит вам ввести возвращающий данные из БД (рис. 10-9).

the procedures Update data adapter toad --.

ftdwnted Рис. Ввод SQL-запроса для новых хранимых процедур В следующем окне можно ввести имена генерируемых мастером хранимых процедур. Кроме того, здесь имеется кнопка Preview SQL Script, при щелчке кото рой открывается диалоговое окно с SQL-сценарием, который мастер Data Adapter Configuration Wizard сгенерировал для создания ваших хранимых процедур (рис 10-10). Если вы создаете приложение для работы с тестовой БД, воспользуйтесь этим диалоговым окном и сохраните SQL-сценарий в файл, чтобы позже выпол нять его в рабочей БД.

382 Часть III Автономная работа с данными: DataSet модели -..

Рис. 10-10. Просмотр сгенерированного мастером для создания новых хранимых процедур По завершении работы с мастером в БД появятся новые хранимые процедуры и объект будет сконфигурирован для работы с ними.

Преимущества и недостатки использования мастера Как уже говорилось, одно из назначений мастера Data Adapter Configuration Wizard Ч сгенерировать логику обновления, упростив и ускорив вам создание эф фективного кода для обновления данных. Мастер предоставляет больше возмож ностей, чем объект Кроме того, он генерирует скучный код, ко торый многие разработчики предпочитают не писать.

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

Но увы, мастер Data Adapter Configuration Wizard несовершенен. В первой версии Studio он работает только с объектами DataAdapter поставщиков OLE DB и SQL Client Data Provider. Кроме того, он предоставляет ограниченные возможности контроля Изменять объекты генерируемые можно, но при изменении конфигурации объекта DataAdapter все эти изменения будут И все же, несмотря на свое несовершенство, мастер Data Adapter Configuration Wizard Ч мощная и полезная утилита.

Прочие проблемы обновления Вы уже изучили основные принципы обновления содержимого БД с использова нием отложенных изменений, хранящихся в объекте DataSet. Если вы генерируе те собственную обновления (в форме запросов UPDATE и DELETE или вызовов хранимых вам необходимо знать чем просто ГЛАВА 10 Передача обновлений в базу данных как управлять параллелизмом, чтобы случайно не перезаписать изменения, сделанные другим пользователем? Как обрабатывать значения NULL при управлении параллелизмом? Как передавать обновления в транзакции? Какую роль играет набор объекта при передаче обновлений?

Подробнее о том, как это Ч в последующих разделах, Способы оптимистичного управления параллелизмом При создании многопользовательского приложения для работы с БД. передающего обновления с применением оптимистичного управления параллелизмом, важно реализовать в обновляющих запросах оптимистичный параллелизма.

Скажем, два пользователя вашего приложение запросили одну и ту же запись дан ных и пытаются обновить ее. Что произойдет? Это зависит от структуры обнов ляющих запросов.

Предлагается четыре основных способа оптимистичного управления парал лелизмом.

только полей первичного ключа SQL-запросы UPDATE и DELETE можно только поля первичного клю ча;

этом возникает ситуация пришедший последним. Обе попытки обновления завершатся успешно. Понятно, что БД не способна поддерживать оба набора обновлений. Должен остаться лишь один. Изменения, сделанные первым будут переопределены изменениями, внесенными последним поль зователем.

Вот кратко последовательность действий при возникновении подобной си туации:

Х пользователь А выбирает запись;

Х пользователь Б выбирает запись;

Х пользователь Б изменяет запись и успешно передает изменения;

Х пользователь А изменяет запись и успешно передает изменения, перезаписы внесенные пользователем Б.

Пользователь А даже не что в период времени между выполнением ис ходного запроса и передачей обновлений в БД соответствующая запись БД была изменена другим пользователем.

Если ситуация побеждает пришедший последним Ч именно что вам способ управления параллелизмом подойдет вам. Однако когда требует ся исключить возможность непреднамеренной перезаписи чужих изменений дру гими пользователями, этот способ неприемлем.

В отличие от мастера Data Adapter Configuration объект не предоставляет такого варианта оптимистичного управления параллелизмом. На вкладке Advanced Options диалогового окна мастера снимите флажок Optimistic Concurrency.

Использование всех полей в разделе WHERE Что, если вариант побеждает пришедший вам не подходит? Напри мер, требуется исключить возможность перезаписи А изменений.

384 Часть III Автономная работа с данными: объект DataSet модели внесенных в БД другими пользователями в период между выполнением пользо вателем А оригинального запроса и передачей им обновлений в БД.

По умолчанию объект и мастер Data Adapter Configuration Wizard в раздел WHERE все поля. Такая логика исключает перезапись изменений, сделанных другими пользователями в интервал времени между тем моментом, когда ваш код выбрал и тем, когда он попытался передать от ложенные изменения этой записи в БД.

Рассмотрим пример. Скажем, пользователи А и Б выбрали одну и ту же запись о клиенте. Пользователь Б изменил значение поля и передал изме нение в БД. При на основе запросов приложение включает в раздел WHERE все и поэтому запрос UPDATE выглядит следующим образом:

UPDATE Customers SET = = Company ContactName = Phone = WHERE = AND CompanyName = Company AND ContactName = Phone = Тем временем А изменил значение поля CompanyName той же записи. Поскольку пользователь А выбрал запись до того, как пользователь Б пе редал измененное значение поля ContactName в БД. запрос UPDATE пользователя А будет выглядеть так;

UPDATE Customers SET CustomerlD = CompanyName = Company ContactName = Phone = WHERE CustomerlD = AND CompanyName = Company AND ContactName = AND Phone = Значение поля ContactName этой записи в БД изменилось, и поэтому ни одна запись таблицы не удовлетворяет критериям раздела WHERE. Следовательно, БД не изменит запись о клиенте. Объект обращается к БД, чтобы узнать число измененных запросом записей, обнаруживает, что нужная запись не откор ректирована, и соответствующим образом помечает объект Подробнее о выявлении и разрешении таких конфликтов Ч в главе Именно этот способ управления параллелизмом использует объект Builder. Мастер Data Adapter Configuration применяет данный способ уп равления параллелизмом по умолчанию.

Примечание Вообще говоря, БД не позволяют сравнивать два В столбце с можно хранить сотни мегабайт информа ции, и сравнение двух окажется весьма неэффективным, если вообще возможным. генерации кода типа объекта и мастера Data Adapter Configuration исключают столбцы с ВЮВ-данными из раздела WHERE обновляющих запросов.

Помните об этом при создании собственной логики обновления.

ГЛАВА Передача обновлений в базу данных Использование полей первичного ключа и полей типа timestamp Поля типа timestamp позволяют упростить раздел WHERE обновляющих запро На самом деле в SQL Server поле типа timestamp содержит не сведения о дате и времени, а двоичные данные, уникальные в пределах БД.

В таблице SQL Server можно определить поле типа timestamp, и при каждом изменении содержимого записи SQL Server будет изменять значение поля типа timestamp этой записи. Давайте добавим в таблицу Customers поле типа timestamp и изменим предыдущий запрос таким образом;

UPDATE Customers SET = = Company = Phone = WHERE CustomerlD AND = Поскольку при каждом обновлении записи сервер генерирует новое значение поля типа timestamp, в разделе WHERE можно воспользоваться комбинацией по лей первичного ключа и поля timestamp и тем самым предотвратить перезапись сделанных другими пользователями.

Большинство БД поддерживают аналогичный тип данных. В одних использу ется уникальное двоичное значение, в других Ч значение Просмот рите документацию вашей СУБД, чтобы узнать конкретный тип данных и опре делить, как заставить БД обновлять соответствующее значение при каждом изме нении содержимого записи.

На настоящий момент ни объект ни мастер Data Adapter Configuration Wizard не поддерживают генерацию логики для данного варианта оптимистичного управления параллелизмом.

Примечание В SQL Server 2000 типы данных и timestamp аналогич ны, и в документации SQL Server рекомендуется использовать ключевое слово rowversion. а не timestamp. Термин timestamp используется здесь потому, что на момент написания данной книги он наиболее широко известен.

Для контроля параллелизма я предпочитаю использовать комбинацию полей первичного ключа и поля типа timestamp, поскольку при этом логика обновле ния гораздо проще, и БД при попытке обновления требуется просматривать меньше полей.

Использование полей первичного ключа и измененных полей По умолчанию ядро курсоров ADO включает в раздел WHERE обновляющих за просов только поля первичного ключа и оригинальные значения измененных лей. Кроме того, в раздел SET запросов UPDATE ядро включает только ные поля.

Рассмотрим наш пример, в котором реализована данная стратегия обновле ния. Предположим, пользователи А и Б одновременно выбрали одну и ту же за пись о клиенте. Пользователь А изменил значение поля CompanyName, а пользо ватель Б Ч значение поля ContactName. Пользователь Б первым передал отложенное поля ContactName. Его запрос UPDATE выглядит так:

386 Часть III Автономная работа с данными: объект DataSet модели UPDATE Customers SET = WHERE = AND = Затем пользователь А передает отложенное изменение поля CompanyName с помощью такого запроса UPDATE:

UPDATE Customers SET CompanyName = Company WHERE AND CompanyName = Company Содержимого записи изменится с CompanyName ContactName ABCDE Original Company Name Original Contact на CustomerlD CompanyName ContactName ABCDE Original Company Name New Contact и затем на CustomerlD CompanyName ContactName ABCDE New Company Name New Contact Оба изменения будут переданы успешно, и изменение пользователя А не пе резапишет внесенное пользователем Б.

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

Теоретически можно написать код, динамически изменяющий структуру со ответствующего объекта Command, и использовать его при обработке события объекта Полагаю, у данной стратегии есть свои преиму щества, однако затраты на ее реализацию перевешивают их.

Работа со значениями NULL В таблице Customers БД есть поле Region, принимающее строки до символов длиной, а также значения NULL. Поле Region многих записей имеет зна чение NULL. Для получения таких записей большинство программистов восполь зуется следующим запросом:

SELECT CustomerlD, CompanyName, ContactName, Phone FROM Customers WHERE = NULL ГЛАВА 10 Передача обновлений в базу этим запросом в ADO.NET или выполнив его в SQL Query Analyzer, вы увидите, что он вернул ноль записей.

Значения NULL Ч особый случай в мире БД, особенно когда дело доходит до сравнения таких значений в запросах. Согласно стандартам сравнивать зна чения NULL с помощью оператора = нельзя. Вместо этого следует оператор IS NULL. Показанный далее запрос возвращает записи таблицы значение поля Region которых Ч NULL:

SELECT Contact Phone FROM Customers WHERE Region IS NULL Что общего имеют значения NULL с передачей обновлений в БД при помощи объекта Давайте кратко обсудим значение свойства объекта Command, созданного нами ранее для передачи измененных записей в таблицу Order Details;

UPDATE [Order Details] SET = ?, = ?, Quantity = ?, = ?

WHERE = ? AND ProductID = ? AND = ? AND UnitPrice = ?

Ни одно из указанных в запросе полей не принимает значений NULL. Как след ствие, раздел WHERE этого запроса относительно прост. Но что, если бы поля Quantity и UnitPrice принимали такие значения? Скажем, вы меняете текущее зна чение поля Quantity, NULL, одной из записей на 20, Если заменить параметры ре альными значениями, запрос будет выглядеть так:

UPDATE Details] SET OrderlD = 12345, = Quantity = 20, UnitPrice = WHERE OrderlD = 12345 AND ProductID = 1 AND Quantity = Null AND UnitPrice = Из-за оператора Quantity = включенного в раздел WHERE, запрос не из менит ни одной записи. Значение поля Quantity нужной нам записи БД Ч Null, однако сравнение Null = Null false, и поэтому БД не изменит ни одной за писи, Так как же изменить раздел WHERE наших запросов и использовать значения при контроле параллелизма? Если определенное поле принимает значения NULL, часть запроса = ?

можно заменить следующей = ? OR IS NULL) AND {? IS Нам нужно, чтобы операция сравнения возвращала когда поле и имеют одинаковое, отличное от NULL значение или оба имеют значение NULL.

объект выбирает из таблицы Customers столбцы и Phone. Поля и не принимают, а поля ContactName и Phone принимают значения NULL. Следова тельно, в разделах WHERE обновляющих запросов следует реализовать проверку па наличие значений NULL. Если вы создаете логику обновления средствами 388 Часть Автономная работа с данными: объект DataSet модели тера Data Adapter Configuration Wizard, то обнаружите, что мастер сгенерировал для передачи изменений следующий код и дополнил его соответствующими про верками на наличие значений NULL:

UPDATE Customers SET = ?, = ?, ContactName = ?, Phone = ?

WHERE = ?} AND (CompanyName = ?) AND = ? OR ((? IS AND (ContactName IS AND ? OR ((? IS NULL) AND (Phone IS Как уже говорилось, мастер Data Adapter Configuration Wizard выполняет очень большую работу по созданию логики обновления. Даже если вы разрабатываете собственную логику, просмотрите сгенерированный мастером код, чтобы проконт ролировать себя и свою работу.

Передача обновлений в транзакциях А если вам требуется передать все изменения в виде отдельной единицы работы, чтобы или все они были успешно переданы в БД, или ни одно из них. Простей шее решение Ч поместить обновления в транзакцию. Тем не менее у объекта нет свойства Объект DataAdapter в действительности не передает изменения. Он лишь пе редает работу объектам Command, хранящимся в Insert Command и Объект Command предоставляет свойство и поэтому для передачи изменений с помощью DataAdapter нужно задать значе ние свойства Transaction объекта используемого объектом как показано ниже:

Visual Basic Dim strConn, strSQL As String = & _ "Initial strSQL = "SELECT Quantity, UnitPrice & "FROM [Order Details] WHERE OrderlD = 10503 & _ BY ProductID" Dim As New Dim As New da As New updating logic the DataAdapter.

соединение и выбираем результаты запроса содержимое объекта DataTable Dim txn As OleDbTransaction = свойства Transaction объектов Command, объектом DataAdapter ГЛАВА 10 Передача обновлений в базу данных = txn = txn изменения и закрываем транзакцию Visual C# string strConn, strConn = + "Initial strSQL = "SELECT ProductID, Quantity, + "FROM [Order Details] WHERE OrderlD = 10503 + "ORDER BY ProductID";

DataTable = new = new OleDbDataAdapter da = new updating logic for the DataAdapter.

соединение и выбираем запроса содержимое объекта DataTable новую txn = значение свойства Transaction объектов DataAdapter txn;

= txn;

= txn;

изменения и закрываем Когда логику обновления генерируют средствами объекта передавать изменения в транзакции несколько сложнее. В действительности со здаваемый экземпляр объекта не генерирует логику обновления.

Он делает это лишь после того, как вы вызовете метод Update. Если вы хотите передавать изменения в транзакции с использованием объекта Command описанная выше модель поведения создает некоторые проблемы, 390 Часть Автономная работа с данными;

объект DataSet модели При попытке передать изменения с использованием аналогичного му кода сгенерирует исключение:

Visual Basic Dim As String Dim As New Dim As New Dim da As New Dim cb As New Dim txn As OleDbTransaction = C# string strConn, tbl = new = new da new cb = new OleDbTransaction txn = При вызове метода объект CommandBuilder выбирает из БД нужные метаданные, используя свойство объекта Мы не сопоставили объект Command в свойстве SelectCommand с только что создан ной транзакцией. объекту не удастся воспользо ваться этим свойством, и он генерирует исключение.

Если добавить перед вызовом метода Update объекта DataAdapter такую стро ку, код успешно выполнится:

= Однако это означает, что CommandBuilder выбирает из БД информацию схе мы в транзакции. Вообще говоря, в течение транзакции следует как можно мень ше трогать данные БД. Более удобный вариант Ч заставить объект CommandBuilder сгенерировать логику обновления перед началом транзакции, вызвав метод (или или Затем, используя следующий код, можно сопоставить сгенерированные объек том CommandBuilder объекты Command с новым объектом Transaction, и DataAdapter передаст обновления в транзакции:

ГЛАВА 10 Передача обновлений в базу данных Visual Basic Dim strConn, As String Dim As New DataTable() Dim As New Dim da As New Dim cb As New Dim txn As OleDbTransaction = = txn = txn = txn Visual string strConn, strSQL;

DataTable tbl = new new da = new cb = new OleDbTransaction txn = = txn;

Transaction = txn;

cb. = txn;

Использование набора В главе 5 рассказывалось, что набор TableMappings объекта опреде ляет, как метод заполняет объект DataSet. В следующем коде при вызове метода создается новый объект DataTable, свойству TableName которого задается значение Visual Basic Dim strConn, strSQL As String strConn = & _ "Initial strSQL = "SELECT ProductID, Quantity, & 392 Часть III Автономная работа с данными: объект DataSet модели "FROM [Order WHERE = 10503 & _ "ORDER BY Dim da As New Dim ds As New Visual C# string strConn, strConn = + "Initial strSQL = "SELECT OrderlD, ProductlD, Quantity, UnitPrice + "FROM [Order Details] WHERE OrderlD = 10503 + "ORDER BY ProductlD";

da = new strConn);

DataSet ds = new Если новому объекту DataTable требуется задать имя Order Details, код можно изменить способами. задать свойству нужное значе ние при помощи перегруженного метода Fill.

Visual Basic Dim da As New 01eDbDataAdapter(strSQL, strConn) Dim ds As New DataSetO "Order Details") Visual C# OleDbDataAdapter da = new strConn);

DataSet ds = new DataSetO;

"Order Во-вторых, добавить элемент в набор TableMappings объекта DataAdapter, что бы последний знал, что сопоставлен с объектом DataTable под именем Order Basic Dim da As New 01eDbDataAdapter(strSQL, strConn) "Order Dim ds As New DataSetO Visual C# OleDbDataAdapter da = new 01eDbDataAdapter(strSOL, strConn);

"Order ГЛАВА Передача обновлений в базу данных DataSet = new При передаче обновлений набор работает аналогичным обра зом. Если методу передать лишь объект на своего набора TableMappings определит, какой объект из соста ва DataSet нужно просмотреть:

Visual Basic Dim da As New "Order логику ds As New DataSetO группу C# OleDbDataAdapter da = new strConn);

логику "Order DataSet ds = new DataSetO;

группу записей Если набор TableMappings объекта DataAdapter не заполнялся, используйте либо метод принимающий объект DataSet и имя либо метод Update, принимающий объект DataTable:

Visual Basic Dim da As New strConn) логику ds As New DataSetO "Order Details") группу "Order Details") Dim da As New strConn) логику обновления Dim As New DataTableC) группу записей 394 Часть III Автономная работа с данными: DataSet модели Visual C# da = new обновления DataSet new "Order группу записей "Order da = new strConn);

логику обновления DataTable = new группу записей Как правило, управлять объектом DataTable, используемым при вызове мето дов и следует посредством одинаковой логики.

Лучший способ обновления предоставляет множество вариантов передачи обновлений. Можно ге нерировать логику обновления в период выполнения при помощи объектов Или предоставить собственную логику обновления в коде, переда вая изменения средствами запросов INSERT, UPDATE и DELETE или вызовов хра нимых процедур. Кроме того, к вашим услугам мастер Data Adapter Configuration Wizard, который позволяет быстро создать логику обновления в период разработки.

Что же выбрать?

Ответ в значительной степени зависит от параметров приложения. Чтобы до стичь максимальной производительности, можно сконфигурировать объекты DataAdapter для передачи обновлений посредством вызовов хранимых процедур.

Однако если приложение должно работать с БД, например Microsoft Access, не поддерживающими хранимые данное решение неприемлемо. Восполь зуйтесь запросами INSERT, UPDATE и DELETE. Все это следует учитывать при вы боре подходящего для приложения способа обновления.

С общей точки зрения, рекомендую вам по возможности передавать измене ния с помощью вызовов хранимых процедур. Если ваш приоритет Ч ствие с различными СУБД, используйте обновления на основе запросов (INSERT, UPDATE и DELETE). Независимо от выбранного вами способа обновления, созда вайте собственную логику Применяйте средства генерации кода типа мастера Data Adapter Configuration Wizard, чтобы сэкономить время на разработ ку, и избегайте генерировать логику обновления в период выполнения. Если из главы вы запоминаете что-то одно, то пусть это будет вот что: используйте объекты в приложениях только при крайней необходимости.

ГЛАВА 10 Передача в базу данных Есть еще несколько сложных случаев о которых я собираюсь нам рассказать. Как выбрать только что сгенерированные значения автоинкремента?

Как передать из объекта содержащего новые и записи, изме нения в несколько связанных таблиц? Как выявить и обработать неудачные по пытки обновления? Как средствами ADO.NET работать с распределенными закциями? Подробнее об этих и других сложных ситуациях Ч в следующей главе.

Особенности объекта Здесь обсуждается объект ADO.NET, и я полагаю, что небесполезно рассмотреть свойства и методы объекта OleDbCommandBuilder.

На одной из особенностей объекта CommandBuilder стоит остановиться но. Объекты OleDbCommandBuilder и от го базового класса. Фактически в первом выпуске объектной модели базовый класс отсутствует, Написание кода, получающего от СУБД необходимые метаданные и преобра зующего их обновления, Ч непростая задача. В противном случае объект был бы не нужен, код, создающий класс CommandBuilder для поставщика данных весьма тривиален и использование объектов в период выполнения приводит к падению производительности, по явление сторонних данных без класса CommandBuilder меня не удивит, Свойства объекта OleDbCommandBuilder В табл. 10-1 свойства объекта Таблица Свойства объекта CommandBuilder Свойство Тип данных Описание DataAdapter DataAdapter объект DataAdapter. для которого CommandBuilder логику обновления Содержит используемый CommandBuilder для имен и столбцов QuoteSuffix String Содержит суффикс, используемый объектом для имен таблиц и Свойство DataAdapter Свойство DataAdapter объекта позволяет просмотреть или изме нить объект DataAdapter. с объектом CommandBuilder.

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

396 Часть III Автономная работа с данными: объект DataSet Методы объекта В табл. 10-2 перечислены методы Таблица 10-2. Методы объекта Метод Описание Возвращает сведения о параметрах объекта щего хранимую Возвращает объект Command с логикой свойства объекта Возвращает объект Command с логикой для свойства объекта Возвращает объект Command с логикой для свойства объекта Указывает объекту CommandBuilder создать обновления заново Метод Объект способен не только генерировать логику обновления для объектов но и получать сведения о параметрах хранимых процедур.

Следующий фрагмент кода с помощью метода получает и выводит сведения о параметрах вызова хранимой процедуры:

Visual Basic Dim strConn As String = & "Initial Dim As New Dim As New = Dim cb As New Dim param As Each In & & Next param Visual C# string strConn;

strConn = + "Initial new cmd = new ГЛАВА 10 Передача обновлений в данных = cb = new in { Console. + + } Если вы пытаетесь создать набор Parameters для объекта щего хранимую процедуру, но не знаете, какие значения задать свойствам Size, Precision и Size, наберите и используйте приведенный выше код в период разра ботки.

Примечание Для использования метода должен быть досту пен объект Connection, определенный для указанного объекта Методы и Методы и GetUpdateCommand позволяют просматривать логику, сгенерированную объектом CommandBuilder.

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

Метод Если в приложении вы изменяете структуру запроса, используемого объектом DataAdapter, вам, пригодится метод RefreshSchema объекта Command Builder.

Когда изменяется значение свойства CommandText запроса для объекта DataAdapter, какие-либо события объекта не После того как CommandBuilder сгенерирует вашу логику ния, его по его мнению, будет Если изменили структуру запроса, используемого объектом DataAdapter и CommandBuilder требуется гене рировать логику обновления заново, можно вызывать метод При этом объект CommandBuilder не приступит к работе сразу же. Будет про сто установлен флаг, указывающий, что текущая логика неправильна. Command Builder сгенерирует логику обновления заново только при вызове метода Adapter или одного из методов объекта CommandBuilder, 398 Часть Автономная работа с данными: объект DataSet модели Вопросы, которые стоит задавать почаще Вопрос. Итак, объект DataAdapter может заполнить объект DataSet результатами запроса и передать отложенные изменения из объекта DataSet в БД. Нужно ли в обоих случаях использовать один и тот же объект Я работаю с мно гоуровневыми приложениями, и, похоже, что между вызовами из клиентского приложения мне требуется на промежуточном уровне поддерживать тельность объектов DataAdapter. Так ли это?

Ответ. Вам предоставлена возможность заполнить объект DataSet и передать изменения в БД при помощи одного и того же объекта однако это не обязательно.

Скажем, у вашего объекта промежуточного уровня имеются два простых ме тода, один из которых возвращает новый объект а другой Ч передает отложенные изменения из DataSet в БД. Для каждого из методов можно задейство вать отдельные объекты DataAdapter. Если вы просто заполняете объект логика обновления не требуется. Если же вы используете DataAdapter только для передачи для него, не требуется определять значение свойства На самом деле для объекта DataAdapter следует определять только те объекты Command, которые будут выполняться. Так, если вы знаете, что DataAdapter будет только передавать новые записи (а не изменять или удалять существующие), можно определить только значение свойства Поскольку DataAdapter не будет выполнять объекты Command, хранящиеся в свойствах и задавать значения этих свойств не требуется.

Единственное исключение из данного правила связано с определением логи ки обновления для объекта DataAdapter при помощи объекта Последнему не удастся сгенерировать логику обновления, если не определено значение свойства SelectCommand объекта DataAdapter.

Вопрос. Мне нужно заполнить объект DataTable результатами соединяющего запроса, изменить содержимое этого объекта и затем передать изменения в БД с помощью объекта DataAdapter. Создать требуемую логику обновления нельзя ни средствами мастера Data Adapter Configuration Wizard, ни объекта CommandButtder.

Как быть?

Ответ. Прежде всего, рекомендую просмотреть раздел главы 7, посвященный соединяющим запросам.

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

SELECT FROM Details] D, Products P WHERE = 10503 AND D.ProductID = ORDER BY P.ProductID ГЛАВА 10 Передача обновлений в базу данных Если выберем результаты запроса в объект DataTable и одну из запи сей, то как следует отредактировать содержимое БД? Нам ответ ясен. Нужно из менить соответствующую запись таблицы Order Details БД Однако для мастера Data Wizard CommandBuilder такой ответ не очевиден.

Ядро курсоров ADO автоматически генерирует для вас логику обновления, даже при работе с соединяющими однако эта логика многих ков разочаровала. Если сгенерировать с помощью показанного запроса объект Recordset ADO и изменить лишь поля, соответствующие таблице Order Details, ядро ADO попытается изменить только соответствующую запись таблицы Order Details.

Однако, если вы захотите заказать другой товар и, чтобы запись данных ото бражалась правильно, отредактируете значения полей ProductlD (таблица Order и (таблица Products), ядро ADO попытается нить значение поля ProductlD в таблице Order и значение поля ProductName в таблице Products. Спорю, что это вам совсем не нужно.

К счастью, в отличие от ADO, не является ящиком и ляет вам создавать собственную логику обновления. В рассматриваемом случае обновления нужно передать только в таблицу Order Details и поэтому можно опре делить собственную логику игнорирующую изменения содержимо го столбца ProductName.

Как создать собственную логику обновления? В случае с соединяющим запро сом ни объект ни мастер Data Adapter Configuration Wizard не окажут вам сколь либо значительной помощи. Однако можно временно опустить поле ProductName, создать нужную логику обновления с помощью любого из этих компонентов и затем добавить поле ProductName в запрос. Окольными путями, зато эффективно.

Вернемся к первой моей рекомендации. В разделе Использование объектов в объектах основанных на главы 7 приво дится фрагмент кода, демонстрирующий, как средствами нескольких объектов DataTable и объекта DataRelation симулировать результаты соединяющего са. Одно из преимуществ такого подхода Ч то, что логика обновления значительно упрощается. Данные объектов DataTable соответствуют отдельным таблицам БД, Объект CommandBuilder и мастер Data Adapter Wizard способны сгенерировать необходимую вам логику обновления.

Вопрос. Вы рассказали об оптимистичном управлении блокировками. Как в использовать пессимистичное управление ими?

Ответ. При пессимистичном управлении блокировками на запись предварительно налагается блокировка. Поскольку содержимое отсоеди нено от БД, простого способа, позволяющего перед изменением записи объекта DataSet наложить блокировку на данные БД, нет. Тем не менее реализовать ана логичную функциональность удается при помощи транзакций, Допустим, пользователь редактирует данные на экране и, чтобы гарантировать передачу этих вам требуется наложить блокировку на ответствующие данные БД. Можно открыть транзакцию и выполнить в ней 400 Часть III Автономная работа с данными: объект DataSet дующий запрос, чтобы наложить блокировку на нужные записи БД и запретить другим пользователям изменять их:

SELECT * FROM [Order Details] WHERE = Примечание Показанный запрос предназначен специально для SQL Server 2000.

Не все БД поддерживают такой синтаксис. Если вы работаете с другими БД, подробнее о блокировке данных в запросах Ч в документации БД.

У этого подхода есть несколько значительных недостатков. Что, если пользо ватель забудет щелкнуть кнопку Changes приложения и пойдет на кухню за пончиком и порцией кофе? Записи БД останутся блокированными. Чем больше данных блокировано и чем дольше, тем ниже масштабируемость приложения.

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

Много лет назад я использовал подобный подход в одном из приложений, но не потому, что был молод или мне требовались деньги. Такая была необходима пользователям этого приложения. Они хотели избежать ситуаций, ког да сделанные невозможно в и данные приходится позже вводить заново.

Один из сотрудников, скажем, Стив (половину сотрудников той компании звали постоянно забывал подтверждать сделанные им изменения. Когда дру гим пользователям не удавалось изменить данные БД, они искали меня, и я искал Стива, на что иногда требовалось довольно много времени. И хотя я объяснял им, что они сами хотели такой функциональности, веселее никому не становилось.

тогда я учился в колледже. Именно в этом возрасте дети экспериментиру ют со типа пессимистического управления блокировками. Я получил хороший урок и никому особо не навредил. Даже Стиву.

Вопрос. Что, если мой объект DataSet содержит столбцы с а мне требуется передавать обновления?

Ответ. Простейшее решение Ч разделить исходный запрос на два, один из ко торых только поля первичного ключа и поле с BLOB-данными, а дру гой возвращает все прочие поля.

Структура запросов, при помощи которых передает статична, и поэтому в разделе SET запроса, заданного свойству объекта используются значения всех полей, даже если измененные ные содержит только одно поле. Эта, казалось бы, маленькая для большинства за просов помеха (и необходимое зло при передаче обновлений средствами храни мой процедуры) может создать значительную проблему при работе с полями, со держащими Почему?

Скажем, мы обрабатываем о сотрудниках и в БД есть таблица Emp loyees, содержащая столбцы с именем, идентификационным номером, должнос тью и фотографией сотрудника. В столбце с фотографиями хранится большой объем двоичной информации Ч содержимое JPEG-файлов.

Если у вас есть объект DataTable с аналогичными столбцами и вам требуется изменить лишь поле отдельной записи, DataAdapter включит в запрос, ГЛАВА 10 Передача в базу данных обновляющий содержимое записи БД, текущие значения всех полей. Это означа ет, что даже при изменении небольшого поля со строковым типом данных в БД все равно передается двоичное содержимое фотографии сотрудника, Другой способ заключается в том, чтобы разделить данные на отдельные таб лицы (рис. На рисунке показаны два объекта между которыми определено отношение на основе объекта Родительский объект включает основные столбцы таблицы Employees Ч LastName и FirstName. Дочерний DataTable объект содержит столбец Photo с ми, а также столбец позволяющий поддерживать связь с ким объектом DataTable.

Employees EmployeelD LastName 1 Nancy 2 Fuller Andrew 3 Janet 4 Peacock Margaret Photo 1 содержимое поля 2 содержимое поля 3 содержимое поля Photo 4 <Двоичное содержимое поля Photo> Рис. Деление содержимого объекта DataTable на основе столбца с Если на основе этой архитектуры создать объект и использовать для каждого объекта DataTable отдельный объект при изменении Title генерируется запрос, обновляющий содержимое БД и не включающий поля Photo. Содержимое поля Photo передается в БД только в том случае, если оно бу дет изменено.

Конечно, если хранить двоичные данные в файле, а в БД Ч лишь путь к нему, вся эта дискуссия Вопрос. Я пытался воспользоваться приводившимся в одном из предыдущих разделов главы фрагментом кода, передающим изменения в таблицу Order Details при помощи объекта однако система выдала ошибку Incorrect syntax near the keyword (некорректный синтаксис ключевого слова Что не так?

402 Часть III Автономная работа с данными: объект DataSet модели Ответ. Не люблю отвечать вопросом на вопрос, но зачем было про бел в имя таблицы? Предвижу возгласы Приятель, я определенно рад сти пробелы в именах таблиц и столбцов. Это сильно упростило мне однако отвечать на них я не стану.

На момент написания этой книги объект не выполняет запрос к БД, чтобы определить, какие и используются для имен таб лиц и столбцов, содержащих пробелы и другие недопустимые символы или пред собой слова. Если вы с помощью объекта генерируете логику обновления для запроса, включающего такие имена таблиц или столбцов, и не определили значения свойств QuotePrefix и этого передать обновления в БД вам не удастся. Что, если вам не хочется задавать значения данных свойств, поскольку ваш код должен нормально рабо тать с разными СУБД? При работе с поставщиком OLE DB Data Provider мож но воспользоваться методом и выбрать из БД символы-разделители. Я протестировал следующий код: он успешно работает со всеми взаимодействующими с Server, Oracle и Access:

Visual Basic Dim strConn, As String = & "Initial strSQL = "SELECT Quantity, & _ "FROM [Order Details] OrderlD = 10503 & _ "ORDER BY ProductID" Dim As New Dim da As New Dim cb As New Dim tblSchema As tblSchema = New {}) = New Dim row As DataRow row = If Not row Is Then = End If row = If Not Is Nothing Then = End If Visual C# string strConn, strSQL;

StrConn = + ГЛАВА 10 Передача обновлений в базу данных "Initial = "SELECT Quantity, UnitPrice + "FROM [Order Details] WHERE OrderlD = 10503 + "ORDER BY ProductID";

OleDbConnection = new OleDbConnection(strConn);

OleDbDataAdapter da = new cb = new DataTable tblSchema = new {});

= new DataRow row = if (row != null) = row = if != null) = этих проблем удастся избежать, исключив из имен таблиц и столб цов недопустимые символы.

Вопрос. Я передаю в БД новые записи с помощью объекта и поля новых записей БД содержат не значения по умолчанию, определенные в БД, а значения null. При использовании ADO поля содержали свои значения по умолчанию. В чем дело?

Ответ. SQL Server и другие БД позволяют определять значения столбцов БД по умолчанию. Как я говорил в главе б, свойство объекта ADO.NET не обеспечивает точного соответствия данной функциональности, и поэтому ADO.NET не генерирует определенные в БД умолчанию ав томатически. Туг сказывается и еще один фактор.

Если вы выполняете запрос, в котором имя поля опущено или вместо ния этого поля указано ключевое DEFAULT, БД автоматически сгенерирует для соответствующего поля новой записи его значение по умолчанию. ADO.NET не позволяет опустить имя поля или указать ключевое слово DEFAULT.

ADO, предшественница ADO.NET, генерирует динамические обновления отдель ных записей. При передаче обновлений ADO исключала из запросов INSERT поля, значения которых не изменялись. Таким образом, новые записи БД, создаваемые средствами ADO, автоматически содержат значения по умолчанию, а записи, со здаваемые средствами ADO.NET Ч нет.

В случае с ADO.NET простейшее решение Ч добавить в приложение код, кото рый при создании новой записи автоматически определял бы значения полей по умолчанию.

Сложные случаи обновления данных 13 10 рассказывалось о изменений в БД с использованием функ ций обновления, предоставляемых объектом Вы научились генери ровать логику обновления мастера Data Adapter Configuration Wizard и объекта Кроме теперь вы понимаете структуру SQL-запро сов UPDATE, UNSERT и DELETE, генерируемых этими утилитами для преобразо вания отложенных изменений, хранящихся в объекте в изменения содер жимого БД.

Примеры главы 10 представляют собой простые случаи обновления данных:

все попытки обновления завершались успешно и передачи изменений не требовалось заново из БД какую-либо информацию. Таблицы, задейство ванные во фрагментах кода, не содержат столбцов с генерируемыми сервером данными (например, значения автоинкремента или значения типа и изменения всегда передаются в одну таблицу, Тем не менее в приложениях, ско рее всего, реализуются более сложные случаи обновления данных, Так, при работе с таблицами, включающими столбцы с автоинкрементом, ве роятно, вам потребуется получать значения автоинкремента, генерируемые БД для новых записей. В других случаях вам может понадобиться повторно выбрать со держимое записи после передачи обновлений БД, например при оптимистич ном управлении параллелизмом на основе полей типа timestamp.

Чем сложнее приложение, тем сложнее возможные ситуации обновления дан ных. Например, непросто передать изменения иерархичных данных. С многоуров невыми приложениями связаны проблемы иного рода Ч передача объектов содержащих только необходимые для передачи обновлений в БД данные, и повторная интеграция только что выбранных значений типа time stamp и автоинкремента в имеющийся объект DataSet.

ГЛАВА Сложные случаи обновления данных Попытки обновления не всегда завершаются успешно. они завершается ошибкой, если другой пользователь успел изменить нужные вам за писи. Рекомендую вам научиться изящно разрешать такие проблемы вместо того, чтобы пытаться любой ценой избежать их возникновения, В этой главе подробно рассматриваются эти и другие сложные случаи обнов ления. Однако здесь я несколько изменил манеру изложения. Предыдущие главы изобилуют фрагментами которые можно копировать и вставлять в консольные приложения и затем, ничего не изменяя, успешно их выполнять. Создать похо жий полнофункциональный кода для сложных случаев обсуждаемых в этой главе, нереально. Поэтому здесь показаны небольшие фрагменты кода приложений, записанных на прилагаемом к книге компакт-диске.

Обновление отображаемого содержимого записи после передачи изменений В главе 10 рассказывалось о создании и использовании запросов INSERT, UPDATE и DELETE для передачи изменений в БД. По сути, эти запросы Ч улица с односто ронним движением. БД изменяет содержимое записи на основе переданной в запросе информации, И хотя она сообщает о числе обработанных запросом за писей, новое содержимое измененных записей БД не возвращает, Иногда требуется, чтобы передача обновлений в БД походила на улицу с двух сторонним движением. Как рассказывалось в главе предотвратить ненамеренную перезапись одним пользователем изменений другого пользователя удается при помощи типа данных timestamp Microsoft SQL Server. Когда содержимое записи изменяется. БД генерирует для нее новое значение поля типа timestamp. Рассмот рим следующую ситуацию.

Ваше приложение отображает содержимое заказа. Пользователь добавляет в заказ новый товар, соответствующий одной из записей вашей таблицы, аналогич ной таблице БД Тем не менее в таблице есть поле типа timestamp, значение которого используется в логике обновления. Когда пользо ватель добавляет новый заказанный товар в БД. та генерирует для новой записи новое значение поля timestamp. Здесь все нормально.

Теперь предположим, что клиентская часть приложения использует а не Web-интерфейс. После того как пользователь передаст в БД новый заказан ный товар, приложение по-прежнему отображает содержимое заказа. Что, если пользователю требуется изменить запись об этом же товаре и снова передать изменение в БД?

Как вы помните, логика обновления объекта использует значение поля timestamp в свойстве При вставке новой записи БД рирует для нее новое значение поля timestamp. Если этого значения не окажется в объекте попытка обновления завершится Аналогичная проблема возникает, если вы, изменив запись, передадите это изменение в БД, и затем снова попытаетесь изменить эту же запись. При внесе нии первого изменения БД новое значение поля timestamp. Если это значение не передать каким-либо способов в объект DataRow, вторая попытка обновления ошибкой.

406 Часть III Автономная работа с данными: DataSet модели Получение новых значений поля timestamp после передачи обновления исходный запрос для выборки данных из таблицы Order Details выглядит так:

SELECT ProductID, Quantity, [Order Details] WHERE OrderlD = ?

Как вы из главы 10. обновления в таблицу можно средства ми следующего параметризованного запроса:

UPDATE [Order Details] SET OrderlD = ?, ?, Quantity = ?, UnitPrice = ?

WHERE OrderlD = ? ProductID = ? AND = ?

А получить новое значение поля timestamp, сгенерированное БД, таким образом:

SELECT TSCol FROM [Order Details] WHERE OrderlD = ? AND ProductID = ?

можно выполнить этот вручную после передачи обновления.

Но что, если нужно передать группу изменений?

Pages:     | 1 |   ...   | 3 | 4 | 5 | 6 | 7 |   ...   | 8 |    Книги, научные публикации