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

David Sceppa Microsoft' ADO.NET Microsoft Press Дэвид Сеппа Microsoft ADO.NET ...

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

LastName = ' 0 ' ' M a l l e y Если вы создаете критерий поиска динамически, обязательно проверьте иско мое значение на наличие символов-разделителей. В этом случае вам пригодится метод Replace класса String. Следующий фрагмент кода создает строку поиска для метода Select и с помощью метода Replace класса String заменяет каждую одиноч ную кавычку в этой строке двумя такими кавычками:

Visual Basic.NET strCriteria = "LastName = '" & strLastName.Replacef, ) & "'" Автономная работа с данными: объект DalaSet модели ADO.NET 294 Часть III Visual C#.NET strCriteria = "LastName = ' " + strLastName.Replace("'", )+ ;

А как указать в критерии поиска дату? Заключите ее в символы *, как показано ниже (к счастью, на символы-разделители в датах можно не обращать внимания).

strCriteria = "OrderDate >= л01/01/2002 AND OrderDate < в02/01/2002й" Иногда требуется заключить в символы-разделитети имена столбцов, исполь зуемые в критерии поиска, Ч например, когда имя содержит пробел или другой символ, не относящийся к алфавитно-цифровым, или похоже на зарезервированное слово типа ЫКЕ или SUM. Так, если имя вашего столбца Ч Space In Name и вам требуется выбрать все записи, значение поля Space In Name которых равно 3, воспользуйтесь следующим критерием поиска:

strCriteria = "[Space In Name] = 3" А что. если имя столбца включает символ-разделитель? Поставьте в критерии поиска перед закрывающим символом-разделителем (]) управляющий символ (\).

Например, если имя вашего столбца Ч Bad]Column[Name и вам требуется выбрать все записи, значение поля Bad]Column[Name которых равно 5, используйте такую строку поиска:

Visual Basic.NET strCriteria = "[Bad\]Column[Name] = 5" Visual C#.NET strCriteria = "[Bad\\]Colunin[Name] = 5";

Примечание Помните, что в С* символ \ является управляющим. В предыду щем фрагменте кода мы фактически задаем переменной strCriteriaFilter строку "[Bad\]Column[Name] = 5" И напоследок, прежде чем перейти от символов-разделителей к следующей теме, рассмотрим фрагмент кода, который по-настоящему изящно справляется с ужас ной ситуацией. Объект DataTable в данном фрагменте кода содержит столбец с действительно некорректным именем, принимающий строки. Код выполняет поиск в этих строках, успешно заключая имя столбца и искомое значение в символы разделители.

Visual Basic.NET Dim tbl As New DataTableQ tbl.Columns.Add("ID", GetType(Integer)) tbl.Columns.Add("Why]would[you ever\use.thistfcolumn/name?", GetType(String)) tbl.LoadDataRow(New Object{) {1, "Thompson"), True) tbl.LoabDataRow(New ObjectO {2, "O'Halley"}, True) ГЛАВА 8 Сортировка, поиск, фильтрация Dim strFilter, strFieldName, strValue As String Dim row As DataRow strFieldName = "Why]would[you ever\use.thisftcolumn/name?" strValue = "O'Malley" strFilter = "[" & strFieldName.ReplaceC"]", "\]") & "] = '" & strValue.Replacef, }& For Each row In tbl.Select(strFilter) Console.WriteLine(row(strFieldName)) Next row Visual C#.NET DataTable tbl = new DataTableO;

tbl.Columns.Add("ID", typeof(int;

tbl.Columns.Add("Why]would[you ever\\use.thisftcolumn/name?' typeof(string));

tbl.LoadDataRow(new object[] {1, "Thompson"}, true);

tbl.LoadDataRow(new object[] {2, "O'Malley"}, true);

string strFilter, strFieldName, strValue;

strFieldName = "Why]would[you ever\\use.this#column/name?";

strValue = "O'Halley";

strFilter = "[" + StrFieldName.ReplaceC"]", "\\]") + "] = ' " + strValue.Replace( )+ ;

foreach (DataRow row in tbl.Select(strFilter)) Console.WriteLine(row[strFieldName]);

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

Использование дополнительных методов Select Как и многие методы объектной модели ADO.NET, метод Select перегружен. Вы можете просто передать строку запроса, а можете и включить в нее порядок сор тировки, а также параметр, определяющий состояние искомых записей (напри мер, только добавленные записи или только измененные записи). Сейчас я вкратце расскажу об этих перегруженных методах.

Указание порядка сортировки В исходном фрагменте кода, использовавшем метод Select, мы искали в объекте DataTable с соответствующими данными информацию о клиентах из США, нахо дящихся вне Сиэтла. Управлять порядком объектов DataRow, возвращаемых ме тодом Select, можно посредством одной из сигнатур перегруженного метода.

В SQL-запросах порядок сортировки данных, возвращаемых запросом, задается в разделе ORDER BY. Так, следующий запрос возвращает список клиентов, отсор тированный по полю City:

296 Часть III Автономная работа с данными: объект DataSet модели ADO.NET SELECT CustomsrID, CompanyName, ContactName, Phone, City FROM Customers ORDER BY City Для сортировки по городам в убывающем порядке замените раздел ORDER BY City на ORDER BY City DESC.

Перегруженный метод Select, как и раздел OltDER BY SQL-запроса, принимает порядок сортировки. Я изменил оригинальный фрагмент кода для сортировки объектов DataRow, возвращаемых методом Select, в убывающем порядке по полю City.

Visual Basic.NET Dim strConn, strSQL As String strConn = "Provider=SQLOLEOB;

Data Source=(local)\NetSDK;

" & "Initial Catalog=Northwind;

Trusted_Connection~Yes;

" strSQL = "SELECT CustomerlD, CompanyName, ContactName, " & "Phone, City, Country FROM Customers" Dim da As New 01eDbDataAdapter(strSQL, strConn) Dim tbl As New DataTableO da.FiU(tbl) strCriteria As String = "Country = 'USA' AND City <> 'Seattle' Dim Dim strSortQrder As String = "City DESC" Dim aRows As DataRow() = tbl.Select(strCriteria, strSortOrder) Dim row As DataRow For Each row In aRows Console.WriteLine(row("CompanyName") & " - " & row("City") 4 _ " - " & row("Country" Next row Visual C#.NET string strConn, strSQL;

strConn = "Provider=SQLOLEDB;

Data Source=(local)\\NetSDK;

" + "Initial Catalog=Northwind;

Trusted_Connection=Yes;

" strSQL = "SELECT CustomerlD, CompanyName, ContactName, " + "Phone, City, Country FROM Customers";

OleDbDataAdapter da = new OleDbDataAdapterfstrSQL, strConn);

DataTable tbl = new DataTableO;

da.Fill(tbl);

string strCriteria = "Country = 'USA' AND City <> 'Seattle'";

string strSortOrder = "City DESC";

Dataflow[] aRows = tbl.Select(strCriteria, strSortOrder);

foreach (DataRow row in aRows) Console.WriteLine(row["CompanyName"] Х+ " - " + row["City"] + " - " + row["Country"]);

Указание нужного состояния искомых записей Как вы помните из главы б, объект DataSet поддерживает кэширование измене ний. Что, если надо выполнить поиск только в измененных рядах объекта DataTable'!

ГЛАВА 8 Сортировка, поиск, фильтрация Воспользуйтесь перегруженным методом Select и укажите значение из перечис ления DataViewRoivState. Можно считать, что это значение Ч фильтр, добавлен ный в критерий поиска. Предположим, вам требуется просмотреть только изме ненные и удаленные записи объекта DataTable. Воспользуйтесь константами Modi fiedOriginal и Deleted из перечисления DataViewRoivState и укажите в качестве па раметров фильтрации и сортировки пустые строки:

Visual Basic.NET Dim dvrs As DataViewRowState dvrs = OataViewRowState.ModifiedOriginal Or DataViewRowState.Deleted Dim aRows As DataflowO = tbl.Select("", "", dvrs) Dim row As Dataflow For Each row In aRows Console.WriteLine(row("CompanyName", DataRowVersion.Original)) Next row Visual C#.NET DataViewRowState dvrs;

dvrs = DataViewRowState.ModifiedOriginal | DataViewRowState.Deleted;

DataRow[] aRows = tt)l.Select("", "", dvrs);

foreach (DataRow row in aRows) Console.WriteLine(row["CompanyNanie", DataRowVersion.Original]);

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

Что представляет собой объект DataView Метод Select объекта DataTable Ч очень мощный и гибкий, но, тем не менее, не является оптимальным решением для всех ситуаций. У него есть два основных ограничения. Во-первых, метод Select принимает динамические критерии поиска и поэтому не может быть сверхэффективным. Во-вторых, Windows- и Web-фор мы не поддерживают связывание с возвращаемым значением метода Select Ч мас сивом объектов DataRow. В ADO.NET предусмотрено решение, обходящее оба этих ограничения, Ч объект DataView.

Объект DataTable модели ADO.NET в целом эквивалентен таблице БД, и поэто му можно предположить, что он аналогичен представлению БД. И хотя у объек тов DataView и представлений БД имеются общие черты, они не настолько похо жи друг на друга, как объекты DataTable и таблицы БД, * Объекты DataView возвращают данные из объектов DataTable У объекта DataVieu1 нет собственной копии данных. При обращении через него к данным объект DataView возвращает данные, хранящиеся в соответствующем объекте DataTable.

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

1-595В Часть III Автономная работа с данными: объект DataSel модели ADO.NET Объекты DataView не являются SQL-запросами Фактически представление БД Ч это запрос. Создавая в БД представление, вы указываете запрос, выполняемый БД для возврата данных представления:

CREATE VIEW ViewCustomersAndOrders AS SELECT C.CustomerlD, C.CompanyName, C.ContactName, C.Phone.

O.OrderlD, 0,EmployeelD, O.OrderDate FROM Customers C, Orders 0 WHERE C.CustomerlD = 0.CustonerlD Объекты DataView модели ADO.NET позволяют фильтровать, сортировать и вести поиск в содержимом объектов DataTable, однако они не являются SQL-запроса ми. С помощью объекта DataView нельзя объединить данные двух объектов Data Table, равно как и просмотреть отдельные столбцы объекта DataTable. Объекты DataView поддерживают фильтрацию запросов на основе динамических критериев, но позволяют обращаться только к отдельному объекту DataTable;

кроме того, через объект DataView всегда доступны все столбцы объекта DataTable.

Имитация соединений при помощи объекта DataRelation Используя объект DataRelation и столбец, основанный на выражении, уда стся имитировать соединение, Например, если у вас есть объекты DataTable с информацией о клиентах и заказах, можно создать отношение между этими объектами и затем добавить в объект DataTable с информацией о заказах основанный на выражении объект DataColumn, чтобы отображать столбец из объекта DataTable с информацией о клиентах:

Visual Basic.NET ds.Relations.Add("CustomersOrde rs", Д ds.Tables{"Customers").Colunms("Customer-ID"), _' ds.Tables("Orders")-CDiuitins("CustoifierID")) tfs.Tab!es{"Orders").Columns.Add("CompanyName", GetTypsfString), _ "Pa rent(CustomersQrders).CompanyName"} Visual C#.NET ds.Relations.Add("CustomersQniers", ds,Tables["Customers"3.Colufflns["Cu8tomerID"3, ds.TablesE"Orders"].ColumnsE"CustoiRerID"3);

ds.Tables["Orders"].Columns.Adtf("CompanyName", typeof(string), "Parent (Customs rsOrders).CoiapanyNaiiie");

Использование объектов DataView в коде Объект DataView предоставляет функциональность, аналогичную возможностям метода Select объекта DataTable. Рассмотрим эту функциональность более подробно и попутно сравним ее с функциональностью метода Select.

ГЛАВА 8 Сортирэвка, поиск, фильтрация Создание объектов DataView Чтобы просмотреть с помощью объекта DataView данные объекта DataTable, его следует связать с этим объектом DataTable. Есть два способа указать объект Data Table, используемый объектом DataView: посредством свойства Table объекта Data View или конструктора этого объекта. Следующие фрагменты кода эквивалентны:

Visual Basic.NET Dim tbl As New Оа1аТаЫе("ИмяТаблицы") Dim vue As DataView vue = New DataView() vue.Table = tbl vue = New DataView(tbl) Visual C#.NET DataTable tbl = new DataTable("HMaTa6rHu.bT);

DataView vue;

vue = new DataView();

vue.Table = tbl;

vue = new DataView(tbl);

Примечание Если вы задаете свойству Table объекта DataVieiv значение Data Table, измените значение по умолчанию свойства TableName объекта Data Table (пустая строка). Не знаю почему, но конструктор объекта DataVieiv не проверяет объект DataTable на соответствие данному ограничению, У объекта DataView также есть конструктор, сигнатура которого более точно со ответствует методу Select объекта DataTable. Этот усовершенствованный конструк тор задает значения свойств Table, RowFilter, Sort и RowStateFilter объекта DataVieit' в одной строке кода. Следующие фрагменты кода эквивалентны:

Visual Basic.NET Dim tbl As New DataTableC'Customers") Dim dvrs As DataViewRowState dvrs = DataViewRowState.ModifiedOriginal Or DataViewflowState.Deleted Dim vue As DataView vue = New DataView vue.Table = tbl vue.RowFilter = "Country = 'USA'" vue.Sort = "City DESC" vue.RowStateFilter = dvrs vue = New DataView(tbl, "Country = ' U S A ' ", "City DESC", dvrs) 300 Часть 111 Автономная работа с данными: объект DataSet модели ADO.NET Visual C#.NET DataTable tbl = new DataTable("Customers"};

DataViewRowState dvrs;

dvrs = DataViewRowState,ModifiedOriginal Or DataViewRowState.Deleted;

DataView vue;

vue = new DataView;

vue.Table = tbl;

vue.RowFilter = "Country = 'USA'";

vue.Sort = "City DESC";

vue.RowStateFilter = dvrs;

vue = new DataView(tbl, "Country = 'USA'", "City DESC", dvrs};

Использование свойства RowStateFilter Свойство RowStateFilter принимает значения из перечисления DataViewRowState (табл. 8-1). Это перечисление можно рассматривать как комбинацию свойства RowState объекта DataRow и перечисления DataRowVersion.

Свойство RowStateFilter работает в качестве двойного фильтра. Например, если задать ему значение ModifiedOriginal, через объект DataView окажутся доступны только измененные записи, и вы будете видеть их оригинальные значения.

Элементы перечисления DataViewRowState Таблица 8-1.

Элемент Описание Отображаются добавленные записи Added Отображаются записи, которые не были удалены (значение по CurrentRows умолчанию) Отображаются удаленные записи Deleted Отображаются измененные записи с их текущими значениями ModifiedCurrent Отображаются измененные записи с их оригинальными значениями ModifiedOriginal Записи не отображаются None Отображаются удаленные, измененные и не изменявшиеся записи OriginalRows с их оригинальными значениями Отображаются записи, которые не изменялись Unchanged Использование объекта DataRowView Если воспользоваться методом Select объекта DataTable к указать константу Modified Original, метод вернет только измененные записи. Тем не менее, как видно из предыдущего фрагмента кода, демонстрирующего работу метода Select, в вызовах возвращаемых объектов DataRow по-прежнему требуется указывать, что нам нужны оригинальные значения полей записи, При использовании объекта DataView этот дополнительный этап не нужен, поскольку DataVieiv возвращает данные с помощью собственного специализиро ванного объекта Ч DataRowView. Функциональность объекта DataRowView в це лом аналогична функциональности объекта DataRow. Кроме того, DataRowView ГЛАВА 8 Сортировка, поиск, фильтрация обладает свойством Item, позволяющим обращаться к содержимому столбца как по имени, так и по порядковому номеру. И хотя свойство Item позволяет просмат ривать и изменять содержимое ряда, через объект DataRowView доступна только одна версия данных записи Ч та, которая указана при помощи свойства DataRow Version этого объекта.

Следующий фрагмент кода посредством объекта DataView возвращает объект DataRoivView и при помощи объекта DataRowView просматривает содержимое записи:

Visual Basic.NET Dim tbl As New DataTable("Customers") Dim vue As DataView vue = New DataView(tbl) Dim row As DataRowView = vue(O) Console.WriteLine{vue("CompanyName")} Visual C#.NET DataTable tbl = new DataTable("Customers");

DataView vue;

vue = new DataView(tbl);

DataRowView row = vue[0];

Console.WriteLlne(vue["CompanyName"]);

Если объект DataRowView не обеспечивает требуемых возможностей, обрати тесь при помощи свойства Row этого объекта к соответствующему объекту DataRow, Просмотр всех записей данных, доступных через объект DataView Доступ к данным объекта DataTable с помощью объекта DataView осуществляется иначе, чем непосредственный доступ к объекту DataTable. Объект DataTable пре доставляет свои записи данных через свойство Rows, позволяющее перемещаться по его содержимому при помощи цикла For Each. У объекта DataView нет похо жего, допускающего простое перечисление набора, через который удавалось бы предоставлять данные.

Объект DataTable предоставляет свойство Count, возвращающее число записей, доступных через объект DataVieic. Используя это свойство, можно создать простой цикл For для просмотра всех записей.

Кроме того, объект DataTable обладает методом GetEnumerator, возвращающим объект lEnumerator. Этот объект, относящийся к пространству имен System.Collec tions, предоставляет функциональность перемещения по записям, аналогичную возможностям метода MoveNext объекта DataReader.

Следующий фрагмент кода демонстрирует, как просмотреть содержимое объек та DataView с помощью свойства Count и метода GetEnumerator.

Автономная работа с данными: объект DataSet модели ADO. NET 302 Часть III Visual Basic.NET Dim tbl As New DataTable("Customers") 'Выбираем данные в объект DataTable и изменяем несколько записей 'Создаем объект OataView, который содержит только измененные записи 'и возвращает их исходное содержимое Dim vue As DataView vue = New DataView(tbl, "", "", DataViewRowState.ModifieclQriginal) Dim row As DataRowView 'Просматриваем содержимое объекта DataView с помощью простого цикла for Dim intCounter As Integer For intCounter = 0 To vue. Count - row = vue(intCounter) Console. WriteLine(row("CompanyName")) Next intCounter 'Просматриваем содержимое объекта DataView, используя метод GetEnumerator Dim objEnum As lEnumerator = vue. GetEnumerator Do While objEnum. MoveNextf) row = CType(objEnum. Current, DataRowView) Console. WriteLine( row( "CompanyName" ) ) Loop Visual C#.NET DataTable tbl = new DataTable("Customers");

//Выбираем данные в объект DataTable и изменяем несколько записей //Создаем объект DataView, который содержит только измененные записи //и возвращает их исходное содержимое DataView vue;

vue = new DataView(tbl, "", "", DataViewRowState.ModifiedOriginal);

DataRowView row;

//Просматриваем содержимое объекта DataView с помощью простого цикла for for (int intCounter = 0;

intCounter < vue. Count;

intCounl:er++) { row = vue[intCounter];

Console.WriteLine(row["CompanyName"]);

//Просматриваем содержимое объекта DataView, используя метод GetEnumerator lEnumerator objEnum = vue.GetEnumerator();

while (objEnum.MoveNextO) \ row = (DataRowView} objEnum. Current;

Console. WriteLine( row[ "CompanyName" ]);

!

ГЛАВА 8 Сортировка, поиск, фильтрация Поиск данных в объекте DataView Как вы уже знаете, свойства RowFilterwRowStateFilter позволяют фильтровать данные в объекте DataView. Кроме того, объект DataView предоставляет методы Find и FindRows, позволяющие искать в нем данные. Эти методы аналогичны методу Find набора Row объекта DataTable, Метод Find Задав значение свойства Sort объекта DataView, вы получите возможность с помо щью метода Find последнего искать ряды по значениям столбцов, перечислен! (ых в свойстве Sort. Как и в случае с методом Find объекта DataRowCollection, одноимен ному методу объекта DataView разрешено передавать одно значение или массив значений, Тем не менее метод Find объекта Dait&iew возвращает не объект DataRow или DataRowVieu.', а значение типа integer, соответствующее порядковому номеру нужной записи в объекте DataView. Если искомая запись не найдена, метод Find вернет -1.

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

Visual Basic.NET Dim strConn As String = "Provider=SQLOLEDB;

Data Source={local}\NetSDK;

" & "Initial Catalog=Northwind;

Trusted_Connection=Yes;

" Dim strSQL As String = "SELECT CustomerlD, CompanyName, ContactName, " & "Phone, City, Country FROM Customers" Dim da As New 01eObDataAdapter(strSQL, strConn) Dim tbl As New DataTable("Customers") da.Fill(tbl) Dim vue As New DataView(tbl) vue.Sort = "ContactName" Dim intlndex As Integer = vue.Find("Fran Wilson") If intlndex = -1 Then Console.WriteLine("How not found!"} Else Console,WriteLine(vue(intlndex)("CompanyName")) End If Visual C#.NET string strConn = "Provider=SQLOLEDB;

Data Source=(local)\\NetSDK;

" + "Initial Catalog=Northwind;

Trusted_Connection=Yes;

";

string strSQL = "SELECT CustomerlD, CompanyName, ContactName, " + "Phone, City, Country FROM Customers";

OleDbDataAdapter da = new 01eDbDataAdapter(strSQL, strConn};

DataTable tbl = new DataTable("Customers"};

da.Fill(tbl);

DataView vue = new DataView(tbl);

vue.Sort = "ContactName";

Автономная работа с данными: объект DataSet модели ADO.NET 304 Часть Int intlndex = vue.Find("Fran Wilson");

if (intlndex == -1) Console.WriteLineC"Row not found!");

else Console,WriteLine(vue[intlndex]["CompanyName"]);

Метод FindRows Метод Find объекта DataRowCollection осуществляет поиск по столбцам, перечис ленным в свойстве PrimaryKey (первичный ключ) объекта DataTable. Поскольку первичный ключ связан с ограничением UNIQUE KEY, по крайней мере одна за пись удовлетворит критериям поиска, переданным методу Find объекта DataRow Collection.

Метод Find объекта DataView осуществляет поиск по столбцам, указанным в свойстве Sort последнего. У многих записей могут быть одинаковые значения полей, используемых для сортировки данных в объекте DataView. Например, при сорти ровке клиентов по полю Country для нескольких записей это поле может иметь значение Spain (Испания). Тем не менее найти посредством метода Find всех клиен тов из Испании нельзя, поскольку он возвращает только целочисленное значение.

К счастью, объект DataView также предоставляет метод FindRows. Его вызыва ют так же, как и метод Find объекта DataView, но метод FindRows возвращает мас сив объектов DataRowView, содержащих записи, которые удовлетворяют вашим критериям поиска.

Следующий фрагмент кода проверяет, нашел ли метод FindRows записи:

Visual Basic.NET Dim vue As New DataView(tbl) vue.Sort = "Country" Dim aRows As DataRowViewf) = vue.FindRows("Spain") If aRows.Length = 0 Then Console.WriteLine("No rows found!") Else Dim row As DataRowView For Each row In aRows Console.WriteLine(row("City")) Next row End If Visual C#.NET DataView vue = new DataView(tbl);

vue.Sort = "Country";

DataRowView[] aRows = vue.FindRowsf"Spain");

if (aRows.Length == 0) Console.WriteLineC"No rows found!");

ГЛАВА 8 Сортировка, поиск, фильтрация else foreach (DataRowView row in aRows) Console.WriteLine(row["City"]);

Модифицирование объектов DataRowView Ряд данных модифицируется с помощью объекта DataRoivVieiv аналогично изме нению содержимого объекта DataRow. Объект DataRowView, как и объект DataRow, предоставляет методы BeginEdit, EndEdit, CancelEdit и Delete.

Создание новой записи данных при помощи объекта DataRowView несколько отличается от создания нового объекта DataRow. У объекта DataView есть метод AddNew, возвращающий новый объект DataRowView. В действительности же но вая запись добавляется в базовый объект DataTable только при вызове метода EndEdit объекта DataRowView.

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

Visual Basic.NET Dim tbl As New DataTable("Customers") Dim vue As New DataView(tbl) 'Добавляем новую запись Dim row As DataRowView = vue.AddNew() row("CustomerID") = "ABCDE" rowC'CompanyName") = "New Company" row("ContactName") = "New Contact" row("Phone") = "(617) 555-1212" row.EndEditO 'Изменяем запись row. BeginEditO rowC'CompanyName") = "Modified" row.EndEditO 'Удаляем запись row.DeleteO Visual C#.NET DataTable tbl = new DataTableC'Customers");

DataView vue = new DataView(tbl);

//Добавляем новую запись DataRowView row = vue.AddNewO;

row["CustomerID"] = "ABCDE";

row["CompanyName"] = "New Company";

row["ContactName"] = "New Contact";

rowfPhone"] = "(617) 555-1212";

Автономная работа с данными: объект DataSet модели ADO.NET Часть III row.EndEdit();

//Изменяем запись row. BeginEditO;

row["CompanyName"] = "Modified" row.EndEdit();

//Удаляем запись row.Delete();

Создание объектов DataView в Visual Studio.NET Создавать объекты DataVieu' гораздо проще, чем объекты DataTable. Вам не нуж но добавлять столбцы и типы данных Ч просто сошлитесь в объекте DataView на DalaTable и задайте нужные свойства (Rou'Filter, RoivStaleFilter, Sort и т.д.).

Добавление нового объекта DataView в конструктор Чтобы добавить новый объект DataView в конструктор, перетащите элемент Data View со вкладки Data панели инструментов Visual Studio.NET в область проекти рования или на панель компонентов. Можно также дважды щелкнуть элемент DataView в панели инструментов.

Задание значений свойств объекта DataView После создания нового объекта DataView следует задать значения некоторых его свойств. Среда Visual Studio.NET упрощает данный процесс. Б окне Properties (рис. 8-1) достаточно выбрать доступный объект DataTable, а также задать значе ния других свойств объекта DataVieic, например RowFilter, RowStateFilter и Sort.

X Forml.vb [Deflfin j Рис. 8-1. Задание значений свойств объекта DataView в Visual Studio.NET ГЛАВА 8 Сортировка, поиск, фильтрация Вот, п о ж а л й, и все.

Особенности объекта DataView Изучив основные возможности объекта DataView, следует подробно рассмотреть его свойства и методы.

Свойства объекта В табл. 8-2 перечислены наиболее часто используемые свойства объекта DataView.

Таблица 8-2. Свойства объекта DataView Свойство Тип данных Описание Указывает, допустимо ли удаление записей AllotvDelete Boolean объекта DataView Указывает, допустимо ли изменение записей AllowEdit Boolean объекта DataVieii' Указывает, допустимо ли добавление записей в AlloivNeu- Boolean объект DataView Указывает, используется ли порядок сортировки ApplyDefaultSort Boolean по умолчанию (первичный ключ) Возвращает число записей в объекте DataVieiv Count Integer (доступно только для чтения) Возвращает ссылку на контейнер DataView'Manager DataViewManagei DataViewManager объекта DataView (доступно только для чтения) Возвращает объект DalaRowView, который заклю Item DataRowVieu Х чает в себе запись данных, доступную через объект DataVieic (доступно только для чтения) Возвращает фильтр, указывающий, какие записи RowFilter String объекта DataTable доступны через объект Data View. Аналогично разделу WHERE SQL-запроса Указывает, какие записи доступны через объект RowStateFilter DataViewRowState DataView. а также версию этих записей Указывает порядок сортировки записей, доступ Sort String ных через объект DataView Возвращает объект DataTable, с которым связан Table DataTable объект DataView Свойства AllowDelete, AllowEdit и Allow/New Объекты DataView нередко используются совместно со связанными элементами управления. При помощи свойств AllowDelete, AllowEdit и AllowNeiv удается легко определить, какие действия пользователя допустимы в таких элементах управле ния. Вместо того чтобы задавать свойства отдельных связанных элементов управ ления, определите соответствующие свойства объекта DataView.

Значение по умолчанию всех этих свойств Ч True, 308 Часть III Автономная работа с данными: объект DataSet модели ADO.NET Свойство ApplyDefaultSort Значение свойства ApplyDefaultSort по умолчанию Ч False. Если задать ему значе ние True, содержимое DataVieu- сортируется по первичному ключу объекта Data Table, связанного с объектом DataVieiv. Кроме того, если вы измените значение свойства ApplyDefaultSort на True, свойству Sort объекта DataView будут заданы стол бцы, составляющие первичный ключ объекта DataTable. Например, если DataView связан с объектом DataTable, который содержит информацию о заказах и первич ный ключ которого состоит из столбцов OrderlD и ProduccID, то при задании свойству ApplyDefaultSort значения True свойству Sort объекта DataView неявно задается значение OrderlD, ProductlD.

Свойства Count и Item Свойство Item параметризовано и возвращает объект DataRouView. Обращаясь к свойству Item, передайте целое число, представляющее номер нужного вам ряда.

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

Следующий фрагмент кода просматривает содержимое объекта DataView с помо щью свойств Count и Item:

Visual Basic.NET Dim tbl As New DataTable("Customers") 'Загружаем данные в объект DataTable и изменяем несколько записей 'Создаем объект DataView, который содержит только измененные записи 'и возвращает их оригинальное содержимое Dim vue As DataView vue = New DataView(tbl) Dim row As DataRowView 'С помощью простого цикла For просматриваем содержимое DataView Dim intCounter As Integer For intCounter = 0 To vue.Count - row = vue(intCounter) Console.WriteLine(row("CompanyName")) Next intCounter Visual C#.NET DataTable tbl = new DataTable("Customers");

//Загружаем данные в объект DataTable и изменяем несколько записей //Создаем объект DataView, который содержит только измененные записи //и возвращает их оригинальное содержимое DataView vue;

vue = new DataView(tbl);

DataRowView row;

//С помощью простого цикла For просматриваем содержимое DataView for (int intCounter = 0;

intCounter < vue.Count;

intCounter++) ГЛАВА 8 Сортировка, поиск, фильтрация row = vue[intCounter];

Console. WriteLine(row["CompanyName"]);

Свойство DataViewManager Если объект DataView создан с помощью метода CreateDataView экземпляра объекта DataViewManager, свойство DataViewManager вернет объект DataViewManager, со здавший ваш объект DataView. В противном случае возвращается неинициализи рованный объект DataViewManager.

Подробнее об объекте DataViewManager Ч в разделе Вопросы, которые стоит задавать почаще* этой главы.

Свойство RowFilter Свойство RowFilter аналогично разделу WHERE SQL-запроса. Через представление доступны только записи, удовлетворяющие заданному в свойстве критерию. Зна чение свойства RowFilter по умолчанию Ч пустая строка.

Простой фильтр на основе столбца со строками:

vue. RowFilter = "Country = 'Spain'" Фильтр на основе шаблона (отображает только те ряды, значение поля CustomerlD которых начинается с Л):

vue. RowFilter = "CustomerlD LIKE "AM" Заключение дат в символы-разделители:

vue. RowFilter = "OrderDate >= #01/01/2002 AND OrderDate < л02/01/2002" Заключение имен столбцов в символы-разделители и обработка символа-разделителя в значении столбца:

vue. RowFilter = "[Spaces In Column Name] = 'Op'Malley'" Свойство RowStateFilter Свойство RowStateFilter двумя способами определяет, какие данные доступны че рез объект DataView. Оно фильтрует объекты DataRow по значению их свойств Rou'State, а также определяет, какая версия ряда доступна через объект DataView.

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

Чтобы задать свойство RowStateFilter, применяют конструктор объекта DataView.

Значение свойства RowStateFilter по умолчанию Ч CurrentRows. При этом через представление доступны все записи объекта DataTable, удовлетворяющие кри терию, заданному в свойстве Sort объекта DataView, и не помеченные как уда ленные.

Часть III Автономная работа с данными: объект DataSet модели ADO. NET Свойство Sort Свойство Sort определяет порядок сортировки данных, доступных через объект DataView, и функционирует практически аналогично разделу ORDER BY SQL-за проса. Порядок сортировки можно.чадать на отдельном поле или группе полей.

По умолчанию ряды сортируются в возрастающем порядке. Для сортировки по лей в убывающем порядке добавьте после имени поля ключевое слово DESC. Если имя поля содержит символ, отличный от буквенно- числового (например, пробел), или является зарезервированным словом, не забудьте заключить его в символы разделители.

Простая сортировка по двум полям (Country, затем City):

vue.Sort = "Country, City" Сортировка в убывающем порядке:

vue.Sort = "OrderDate DESC" Заключение имени поля в символы-разделители:

vue.Sort = "[Space In ColumnName]" Значение свойства Sort по умолчанию Ч пустая строка;

при этом все содержи мое объекта DataView выводится в порядке, соответствующем порядку базового объекта DataTable. Задать свойство Sort можно посредством конструктора объек та DataView, Свойство Table Свойство Table позволяет задать или обратиться к объекте DataTable, с которым связан объект DataView. Если изменить значение свойства Table, свойствам RowFilter и RowStateFtiter объекта DataView задаются соответствующие значения по умол чанию.

Чтобы Задать свойство Table, используют конструктор объекта DataView.

На момент написания данной книги свойству Table не разрешалось задавать объект DataTable, свойство TableNatne которого Ч пустая строка;

в противном случае генерировалось исключение.

Методы объекта DataView Методы объекта DataView описаны в табл. 8-3.

Таблица 8-3. Методы объекта Метод Описание Создает новый объект Временно кэширует изменения содержимого объекта DataView Beginlnit Копирует объекты DataRoicView в массив СоруТо Помечает объект DataRowView как удаленный Delete Подтверждает внесение кэшированных изменений в объект DataView Endlnit Выполняет в объекте DataView поиск отдельной записи /ui Find ГЛАВА 8 Сортировка, поиск, фильтрация Метод Описание Выполняет в объекте DataView поиск нескольких записей данных FindRows Возвращает объект lEnumeralor для перечисления записей, доступ GetEnumerator ных через объект DataView Методы AddNew и Delete Методы AddNew и Delete позволяют добавлять и удалять записи данных из базо вого объекта DataTable. Метод AddNew возвращает новый объект DataRowView. Задав значения нужных полей, вы можете вызвать метод EndEdit объекта DataRowView и добавить запись данных в базовый объект DataTable.

Метод Delete принимает порядковый номер записи в объекте DataView и по зволяет удалить эту запись. Если у вас есть ссылка на объект DataRow или Data RowView, для удаления записи стоит применить метод Delete одного из этих объек тов. Помните, что в этом случае запись просто помечается как удаленная. Чтобы на самом деле удалить ее из объекта DataTable, вызовите Mtro^AcceptCbanges (объек та DataRow, DataTable или DataSet, содержащего ряд) или передайте изменения в БД с помощью объекта DataAdapter.

Методы Beginlnit и Endtnit Если вам необходимо изменить значения нескольких свойств объекта DataView, но вы не хотите, чтобы эти изменения сказались на доступных через этот объект данных, пока вы не измените значения всех нужных свойств, воспользуйтесь ме тодами Beginlnit и Endlnit, Представьте, например, что объект DataView связан с определенным объектом DataTable и значение свойства RowFilter объекта DataView таково, что через по следний доступна лишь небольшая часть записей данных. Содержимое объекта DataView выводится в элементе управления DataGrid Windows-форме, и по указа нию пользователя вам требуется изменять значения свойств Table и RowFilter объек та DataView. В данной ситуации код, изменяющий значения свойств объекта Data View, следует поместать в вызовы методов Beginlnit и Endlnit объекта DataView, чтобы запретить элементе управления DataGrid моментально отображать все записи нового объекта DataTable, Метод Сору То Объект DataView предоставляет метод С ору То. Он функционирует аналогично методу СоруТо объекта Art-ay и позволяет копировать объекты DataRowView, до ступные через объект DataView. в массив.

Примечание Возможно, разработчики, имеющие опыт работы с ОАО, RDO и ADO, сочтут, что метод СоруТо похож на метод GetRows, возвращающий содержимое структуры данных в виде двухмерного массива. Увы, это не так.

Если честно, я не знаю, чем вам поможет массив объектов DataRowView. Одна ко все же давайте рассмотрим фрагмент кода, в котором используется метод СоруТо, на случай, если кто-то сумеет найти ему применение. Вероятно, этот человек вспом нит, как помог ему данный фрагмент кода, и поблагодарит меня. Я принимаю и наличные.

312 Часть III Автономная работа с данными: объект DataSet модели ADO.NET Visual Basic.NET Dim tbl As New DataTable("Customers") Dim viie As DataView vue = New DataView(tbl) Dim aRows As DataRowVlew() aRows-= Array.CreateInstance(GetType(DataRowView), vue.Count) vue,CopyTo(aflows, 0) Visual C#.NET DataTable tbl = new DataTable("Customers");

DataView vue;

vue = new DataView(tbl);

DataRowView[] aRows;

aRows = Array,CreateInstance(typeof(DataRowView), vue.Count);

vue.CopyTo(aRows, 0);

Методы Find и FindRows Методы Find и FindRoivs позволяют искать записи данных в объекте DataView. Они оба перегружены и принимают отдельное значение или массив значений. Объект DataView на основе указанных значений осуществляет поиск среди своего содер жимого по столбцам, перечисленным в свойстве Sort:

Visual Basic.NET Dim strConn As String = "Provider=SQLOLEDB;

Data Source=(local)\NetSDK;

" & "Initial Catalog=Northwind;

Truste'd_Connection=Yes;

" Dim strSQL As String = "SELECT CustomerlD, CompanуName, ContactName, " & "Phone, City, Country FROM Customers" Dim da As New 01eDbDataAdapter(strSQL, strConn) Dim tbl As New OataTableC'Customers") da.Fill(tbl) Dim vue As New DataView(tbl) Console.WriteLine("Use the Find method to locate a row " & "based on the ContactName column") vue.Sort = "ContactName" Dim intlndex As Integer = vue.Find("Fran Wilson") If intlndex = -1 Then Console.WriteLine(vbTab & "Row not found!") Else Console.WriteLine(vbTab & vue(intIndex}("CompanyName" End If Console, WriteLineO Console.WriteLineC'Use the FindRows method to locate rows " & "based on the Country column") vue.Sort = "Country" ГЛАВА 8 Сортировка, поиск, фильтрация Dim aRows As DataRowView() = vue.FindRows("Spain") If aRows.Length = 0 Then Console.WriteLine(vbTab & "No rows found!") Else Dim row As DataRowView For Each row In aRows Console.WriteLine(vbTab & row("City")) Next row End If Visual C#.NET string strConn = "Provider=SQLOLEDB;

Data Source={local)\\NetSDK;

" + "Initial Catalog=Northwind;

Trusted_Connection=Yes;

";

string strSQL = "SELECT CustomerlD, CompanyName, ContactNarne, " + "Phone, City, Country FROM Customers";

OleDbDataAdapter da = new 01eDbDataAdapter(strSQL, strConn);

DataTable tbl = new DataTable("Customers");

da.Fill(tbl);

DataView vue = new DataView(tbl);

Console.WriteLine("Use the Find method to locate a row " + "based on the ContactName column");

vue.Sort = "ContactName";

int intlndex = vue.Find("Fran Wilson");

if (intlndex == -1) Console.WriteLine("\t" + "Row not found!");

else Console.WriteLine("\t" + vue[intIndex]["CompanyName"]);

Console. WriteLineO;

Console.WrlteLine("Use the FindRows method to locate rows " + "based on the Country column");

vue.Sort = "Country";

DataRowView[] aRows = vue.FindRows("Spain");

if (aRows.Length == 0) Console.WriteLine("\t" +Х "No rows found!");

else foreach (DataRowView row in aRows) Console.WriteLine("\t" + row["City"]);

Метод GetEnumerator Метод GetEnumerator предоставляет еще один способ просмотреть содержимое объекта DataView. Он возвращает экземпляр объекта lEnumerator, расположенно го в пространстве имен System.Collections.

Метод MoveNext объекта lEnumerator аналогичен методу Read объекта DataReader и возвращает логическое значение, указывающее, доступен ли следующий объект набора. Свойство Current возвращает текущий объект с универсальным типом данных Object. Следующий фрагмент кода преобразует вывод в объект DataRowVieir.

314 Часть III Автономная работа с данными: объект DataSet модели ADO. NET Visual Basic.NET Dim tbl As New DataTable("Customers") Dim vue As DataView(tbl) Dim row As DataRowView Dim objEnum As lEnumerator = vue.GetEnumerator Do While objEnum.HoveNextO row = CType(objEnum. Current, DataRowView) Console. WriteLine( row( "CompanyName" ) ) Loop Visual C#.NET DataTable tbl = new DataTableC'Customers");

DataView vue = new DataView(tbl);

DataRowView row;

lEnumerator obJEnum = vue.GetEnumerator();

while (objEnum.MoveNextO) t row = (DataRowView) objEnum. Current;

Console. WriteLine( row[ "CompanyName" ] };

Событие ListChanged объекта DataView У объекта DataView есть событие ListChanged, наступающее при изменении содер жимого объекта DataView, например при добавлении, удалении или изменении ряда данных, доступного через объект Data-View-, при заполнении объектом DataAdapter базового объекта DataTable;

при изменении значений свойств RowFtlter, RoivState Filter, Sort или Table объекта DataView. Вот пример использования этого свойства:

Visual Basic.NET Dim vue As New DataViewO AddHandler vue, ListChanged, vue_ListChanged Private Sub vue_ListChanged(ByVal sender As Object, ByVal e As ListChangedEventArgs) Console. WriteLine("ListChanged - " & e. ListChangedType. ToSt ring ( ) ) End Sub Visual C#.NET //предполагается наличие using System. ComponentModel DataView vue = new DataView;

vue, ListChanged += new ListCnangedEventHandler(vue_ListChanged);

private void vue_ListChanged(object sender, ListChangedEventArgs e) ГЛАВА 8 Сортировка, поиск, фильтрация Console.WriteLine("ListChanged - " + е. ListChangedType.ToStringQ);

Свойства объекта DataRowView В табл. 8-4 перечислены свойства объекта DataRowView;

большинство которых доступны только для чтения.

Таблица 8-4. Свойства объекта DataRowView Описание Тип данных Свойство Возвращает объект DataView, к которому относится DataView DataView объект DataRowView. Доступно только для чтения Указывает, изменяют ли запись в данный момент. До IsEdit Boolean ступно только для чтения Указывает, является ли запись новой ожидающей запи IsNew Boolean сью- Доступно только для чтения Возвращает/задает содержимое поля Item Object Возвращает для объекта DataRowView соответствующий DataRow Row объект DataRow. Доступно только для чтения Сообщает, какая версия соответствующего объекта DataRowVersion RowVersion DataRow доступна через объект DataRowView. Доступно только для чтения Свойство DataView Возвращает объект DataView, к которому относится объект DataRowView.

Свойства IsEdit и IsNew Свойства IsEdit и IsNew позволяют определить, редактируется ли в данный момент объект DataRowView, и если да, то как именно.

Если вы редактируете новую запись (создали новый объект DataRowView с помощью метода DataViewAddNew, но еще не вызвали метод EndEdit, чтобы доба вить запись в базовый объект DataTable), метод IsNew вернет True, а метод IsEdit Ч False. При редактировании уже имеющейся записи таблицы метод IsNew возвра щает False, а метод IsEdit Ч True.

Свойство Item Функциональность свойства Item объекта DataRowView во многом аналогична функциональности одноименного свойства объекта DataRow. Свойство Item объекта DataRowView позволяет изменять и просматривать содержимое поля соответству ющей записи. Обращаться к полю посредством свойства Item можно по его име ни или порядковому номеру.

Свойство Row Функциональность объекта DataRowView немного уже функциональности объек та DataRow. Так, у DataRowView нет методов типа AcceptChanges и GetCbanges. Если вам необходимы возможности интерфейса DataRow, воспользуйтесь свойством Row Часть III Автономная работа с данными: объект DataSet модели ADO.NET объекта DataRoivView. Оно возвращает соответствующий объекту DataRowView объект DataRow.

Свойство RowVersion Если вы работаете с записью данных при помощи интерфейса DataRoivView и вам требуется определить, какая именно версия данных доступна через свойство Item, просмотрите значение свойства RowVersion объекта DataRowView.

Свойство RowVersion доступно только для чтения, о но возвращает значение из перечисления DataRowVersion.

Методы объекта DataRowView Методы объекта DataRoivView описаны в табл. 8-5.

Таблица 8-5. Методы объекта DataRowView Метод Описание Начинает процесс редактирования записи Отменяет отложенные изменения записи Создает новый объект DataView, содержащий только дочерние записи текущей записи Помечает запись как удаленную Сохраняет отложенные изменения записи Методы BeginEdit, CancelEdit и EndEdit Методы BeginEdit, CancelEdit и EndEdit объекта DataRowView работают аналогич но соответствующим методам объекта DataRow. Если перед редактированием за писи вы вызовете метод BeginEdit, сделанные изменения подтверждаются только после вызова метода EndEdit. Для их отмены воспользуйтесь методом CancelEdit.

Метод CreateChildView Предположим, вам нужно создать объект DataView, отображающий только дочер ние записи, связанные с конкретной записью. Для отношения клиенты Ч зака зы, о котором идет речь в этой книге, создать подобный объект DataView очень легко. Назначьте свойству Table объект DataTable, содержащий информацию о заказах, а свойству RowFilter Ч строку типа CustomerlD = 'ALFKI\ Все это выглядит весьма просто. А что, если придется проверить значение стол бца (CustomerlD) на предмет сим волов-разделителей? А если вы имеете дело с отношением, основанным на группе столбцов?

Объект DataRowView предоставляет простое и изяшное решение этой пробле мы Ч метод CreateChildView. Вам следует лишь вызвать его и указать имя отноше ния или объект DataRelation (так же, как и при работе с методом GetChildRows объекта DataRow). Метод CreateChildView вернет новый объект DataView, исполь зующий данное отношение в качестве фильтра.

Следующий фрагмент кода демонстрирует, как работает метод CreateCbildView-.

Сортировка, поиск, фильтрация ГЛАВА Visual Basic.NET Dim ds As New DataSetO Dim tblCustomers, tblOrders As DataTable tblCustomers = ds.Tables("Customers") tblOrders = ds,Tables("Orders") ds,Relations.Add("CustomersOrders", tblCustomers.Columns{"CustomerID"), tblOrders.Columns("CustomsrID")) Dim vueCustomers, vueOrders As DataView vueCustomers = New DataView(tblCustomers) vueOrders = vueCustomers(0}.CreateChildView("CustomersOrders") Visual C#.NET DataSet ds = new DataSetO;

DataTable tblCustomers, tblOrders;

tblCustomers = ds,Tables["Customers"];

tblOrders = ds.Tables["Orders"];

ds.Relations.Add("CustomsrsOrders", tblCustomers.Columns["CustomerID"], tblOrders.Columns["CustomerID"]);

DataView vueCustomers, vueOrders;

vueCustomers = new DataView(tblCustoiners);

vueOrders = vueCustomers[0].CreateChildView("CListoinersOrders");

Примечание При создании нового объекта DataView с помощью метода Create CbildView свойство RowFilter возвращает пустую строку1. Тем не менее при этом доступны только нужные дочерние записи. Как это объяснить?

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

Метод Delete Метод Delete объекта DataRowView позволяет удалить запись. Помните, что запись лишь помечается как удаленная, на самом деле она по-прежнему присутствует в объекте DataTable. Чтобы реально удалить запись из объекта DataTable, вызовите метод AcceptChanges или передайте ожидающие изменения в БД с помощью объекта DataAdapter.

Вопросы, которые стоит задавать почаще Вопрос. Как выбрать наиболее подходящий метод для поиска данных в объекте DataTable?

Ответ. Это зависит от того, что вы хотите искать и что собираетесь делать с ре зультатами поиска. Бот несколько рекомендаций:

Часть III Автономная работа с данными: объект DataSet модели ADO.NET 31 Х для поиска записи по значению первичного ключа используйте метод Data Table Rows find;

Х чтобы связать элементы управления с записями, удовлетворяющими критери ям поиска, применяйте объект DataView;

Х для многократного поиска по столбцам, не являющимся ключевыми, исполь зуйте метод DataViewFind;

Х во всех остальных случаях годится метод DataTable.Select, Вопрос. Создать объект DataView в коде достаточно просто. А можно ли полу чить какие-нибудь преимущества, создавая этот объект средствами Visual Studio.NET в период разработки?

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

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

lhalPMiNMCiilt.

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

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

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

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

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

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

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

Нам нужно определить порядковый номер требуемой записи в объекте DataVieu;

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

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

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

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

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

Visual Basic.NET Dim tbl As New DataTable("Customers") Dim vueByCountry As New DataView(tbl) vueByCountry.Sort = "Country" Dim vueByContactName As New DataView(tbl) vueByContactName.Sort = "ContactName" Dim intlndexCountry, intlndexContactName As Integer Автономная работа с данными: объект DataSel модели ADO.NET 320 Часть intlndexCountry = - IntlndexContactName = vueByContactName.Find("Aria Cruz") Dim row As Dataflow = vueByContactName(intIndexContactNai?ie), Row Dim strCountry As String = rowf'Country") Visual C#.NET DataTable tbl = new OataTable("Customers");

DataView vueByCountry = new DataView(tbl);

vueByCountry.Sort = "Country";

DataView vueByContactName = new DataView(tbl);

vueByContactName. Sort = "ContactName";

int intlndexCountry, intlndexContactName;

intlndexCountry = -1;

intlndexContactName = vueByContactName.Find("Aria Cruz");

Dataflow row = vueByContactName[intIndexContactName].Row;

sthing strCountry = row["Country"];

Это самая простая часть. Дальше все усложняется.

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

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

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

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

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

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

Visual Basic.NET Dim intStartingPoint As Integer = vueByCountry.Find(row("Country")) Dim intCounter As Integer = intlndexStartingPolnt ГЛАВА 8 Сортировка, поиск, фильтрация Do If vueByCountry(intCounter).Row Is row Then intlndexCountry = intCounter blnFound = True Exit Do End If intCounter += Loop While intCounter < vueByCountry.Count And _ vueByCountry(intCounter)("Country") = strCountry If Not blnFound Then intCounter = intlndexStartingPoint - Do While intCounter >= 0 And vueByCountry(intCounter)("Country") = strCountry If vueByCountry(intCounter).Row Is row Then intlndexCountry = intCounter blnFound = True Exit Do End If intCounter -= Loop End If If blnFound Then Console.WriteLine(vueByCountry(intIndexCountry)("CompanyNarne"}) Else Console.WriteLineC'Not found!") End If Visual C#.NET int intStartingPoint = vueByCountry.Find(row["Country"]);

while {intCounter < vueByCountry.Count i& vueByCountry[intCounter]["Country"],EqualsCstrCountry)) { if (vueByCountry[intCounter].Row == row) { intlndexCountry = intCounter;

blnFound = true;

break;

} intCounter++;

i if (IblnFound) { intCounter = intStartingPoint - 1;

while (intCounter >= 0 && vueByCountry[intCounter]["Country"].Equals(strCountry { if

blnFound = true;

break;

) intCounter-;

) if (blnFound) Console.WriteLine(vueByCountry[lntIndexCountry]["CompanyName"]);

else Console.WriteLine("Not found!");

ГЛАВА Работа с объектами DataSet со строгим контролем типов О предыдущих трех главах рассказывалось о создании и использовании объек тов DataSet. Как показано ниже, код для доступа к содержимому DataSet в программ ном плане аналогичен коду для доступа к объектам, которые использовались рань ше, например к объекту Recordset моделей ADO и ОАО:

ADO.NET и Visual Basic.NET txtCompanyName.Text = ds.Tables("Customers").Rows(0)("CompanyName") ADO.NET и Visual C#.NET txtCompanyName.Text = ds.Tables["Customers"].Rows[0]["CompanyName"];

ADO, DAO и классический Visual Basic txtCompanyName.Text = rs.Fields("CompanyName").Value Разработчики писали подобный код, начиная с первых дней существования Visual Basic. Технически он безупречен Ч работает отлично. Тем не менее это не значит, что усовершенствовать старые методики программирования нельзя.

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

Visual Basic.NET txtCompanyNafne.Text = ds.Customers(O). CompanyName 324 Часть 111 Автономная работа с данными: объект DataSet модели ADO.NET Visual C#.NET txtCompanyName.Text = ds.Customers[0].CompanyName;

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

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

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

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

Visual Basic.NET Dim strConn, strSQL As String strConn = "Provider=SQLOLEDB;

Data Source=(local)\NetSDK;

" 4 _ "Initial Catalog=Northwind;

Trusted_Ccmnection=Yes;

" Dim en As New QleDbConnection(strConn) strSQL = "SELECT CustomerlD, CompanyName, ContactNaroe, Phone " & "FROM Customers" Dim daCustomers As New OleDbOataAdapterfstrSQL, en) StrSQL = "SELECT OrderlD, CustomerlD, Employee!D, OrderDate " & Работа с объектами DataSet со строгим контролем типов ГЛАВА "FROM Orders" Dim daOrders As New 01eDbDataAdapter(strSQL, en) Dim ds As New DataSetO ds.DataSetName = "Chapter9" cn.OpenO daCustomers.FillSchema(ds, SchemaType.Source, "Customers") daOrders.FillSchema(ds, SchemaType.Source, "Orders") cn.CloseO ds.Relations.Add("CustomersOrders", ds.TablesC"Customers").Columns("CustomerlD"), ds.TablesC"Orders").Columns("CustomerlD")) ds.WriteXmlSchemaC"C:\Chapter9.XSD") Visual C#.NET string strConn, strSQL;

strConn = "Provider=SOLOLEDB;

Data Source=(local)\\NetSDK;

" + "Initial Catalog=Northwind;

Trusted_Connection=Yes;

";

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

OleDbDataAdapter daCustomers = new 01eDbDataAdapter(strSQL, strConn);

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

OleDbDataAdapter daOrders = new 01eDbDataAdapter(strSQL, strConn);

DataSet ds = new DataSetO;

ds.DataSetName = "Chapters";

cn.OpenO;

daCustomers.FillSchema(ds, SchemaType.Source, "Customers");

daOrders.FillSchema(ds, SchemaType.Source, "Orders");

cn.CloseO;

ds.Relations.Add("CustomersOrders", ds.Tables["Customers"].ColumnsC"CustomerlD"], ds.Tables["Orders"].Columns["CustomerID"]);

ds.WriteXmlSchema<"C:\\Chapter9.XSD");

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

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

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

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

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

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

У утилиты XML Schema Definition Tool есть также и другие параметры. Они описаны в документации.NET Framework SDK;

кроме того, их можно просмотреть, набрав в командной строке XSD /?.

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

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

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

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

Для начала создайте приложение Microsoft Windows на удобном вам языке. Затем воспользуйтесь вкладкой Data панели инструментов и добавьте на Windows-форму два объекта QleDbDataAdapter. С помощью мастера Data Adapter Configuration Wizard свяжите оба объекта OleDbDataAdapter с БД Northwind. В окне Generate SQL State ment мастера введите следующие SQL-операторы:

SELECT CustomerlD, CompanyName, ContactName, Phone FROM Customers SELECT OrderlD, CustomerlD, EmployeelD, OrderDate FROM Orders Щелкните в свободной области конструктора правой кнопкой и выберите Generate Dataset. Откроется диалоговое окно Generate DataSet (рис. 9-1). Введите имя нового класса DataSet. Chapter9;

и щелкните ОК.

Generate Detaset Generate а dataset that Includes the specified tables.

Choose a dataset:

Existing chapters Choose which tabte() to add to the datasetc V. Customers (QleDbDataAdapterl) V Orders (OleDbDataAdapter 2) Add this dataset to Che design*-.

Рис. 9-1- Создание нового класса DataSet со строгим контролем типов И вот вы одним махом создали сразу файл схемы DataSet и класс DataSet со строгим контролем типов. Единственное отличие класса DataSet, созданного сейчас, от класса, созданного нами ранее, Ч то, что у первого нет объекта DataRelation.

Пока нет.

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

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

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

328 Часть III Автономная работа с данными: объект DataSet модели ADO.NET ТО (Wine l lelatmst (Wyrtf), stlect the patent element and ksy, wither, мЫ Ни гШ f*M ea^espondme to each parent ftsM, Рис. 9-2. Добавление объекта DataRelation в класс DataSet Чтобы создать новый класс DataSet со строгим контролем типов, среда Visual Studio.NET выполнила ряд действий.

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

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

4. Добавила в проект.xsd-файл.

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 и OrdersDataTable) предоставляют свойство по умол чанию Пет, возвращающее специфичный для таблицы класс, происходящий от DataRow.

Visual Basic.NET Dim ds As New Chapter9() OleDbDataAdapten.Fill(ds) Dim tblCustomers As Chapter9,CustomersDataTable = ds.Customers Dim rowCustomer As Chapter9.CustomersRow = tblCustomers(O) Visual C#.NET Chapters ds = new Chapter9();

OleDbDataAdapterl.Fill(ds);

Chapter9.CustomersDataTable tblCustomers = ds.Customers;

Chapters,CustotnersRow rowCustomer = tblCustomers[0];

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

Рассмотрим несколько примеров, которые позволят сравнить работу с данными при помощи обычных объектов DataSet и объектов DataSet со строгим контро лем типов, Добавление записи Все классы, соответствующие объектам DataTable из состава DataSet, позволяют добавлять запись в новый экземпляр DataTable двумя способами. Метод New-^Имя ТаблицыЖогс возвращает для вашего экземпляра DataTahle новый объект DataRow со строгим контролем типов. Затем вы можете воспользоваться свойствами это го объекта DataRow и задать значения полей записи, как показано ниже, Visual Basic.NET Dim ds As New Chapter9() Dim tblCustomers As Chapter9.CustomersDataTable = ds.Customers Dim rowCustomer As Chapter9.CustomersRow rowCustomer = tblCustomers.NewCustomersRow{) rowCustomer. CustonterlD = "ABCDE" rowCustomer.CompanyName = "New Company" rowCustomer.ContactName = "New Contact" rowCustomer.Phone = "(800) 555-1212" tblCustorners.AddCustofnersRow( rowCustomer) 'Вместо Dim rowCustomer As DataRow = tblCustomers.NewRowQ rowCustomerC'CustomerlD") = "ABCDE" rowCustomer("CompanyName") = "New Company" rowCustomerC'ContactName") = "New Contact" Автономная работа с данными: объект DataSet модели ADO.NET Часть (II rowCustomer{"Phone") = "(800) 555-1212" tblCustomers,Rows.Add(rowCustamer) Visual C#.NET Chapter9 ds = new Chapter9();

Chapter9.CustomersDataTable tblCustomers = ds.Customers;

Chapter9.CustomersRow rowCustomer = tblCustomers.NewCustomersRowf);

rowCustomer.CustomerlD = "ABCDE";

rowCustomer.CompanyName = "New Company";

rowCustomer.ContactName = "New Contact";

rowCustomer.Phone = "(800) 555-1212";

tblCustomers.AddCustomersRowfrowCustomer);

//Вместо DataRow rowCustomer = tblCustomers.NewRowO;

rowCustomer["CustomerID"] = "ABCDE";

rowCustomer["CompanyNarce"] = "New Company";

rowCustomer["ContactName"] = "New Contact";

rowCustomer["Phone"] = "(800) 555-1212";

tblCustomers.Rows.Add(rowCustomer);

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

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

Как и метод Add набора DataRowCottection, метод А&а<ИмяТаблицы>Кои: объе кта DataTable со строгим контролем типов перегружен. В следующем фрагменте кода используется объект DataTable с именем Customers из состава созданного нами ранее объекта DataSet:

Visual Basic.NET Dim ds As New Chapter9() Dim tblCustomers As Chapter9.CustomersDataTable = ds.Customers tblCustomers,AddCustomersRow("ABCDE", "New Company", "New Contact", "(800) 555-1212") 'Вместо tblCustorners.Rows.AddfNew ObjectO {"ABCDE", "New Company", _ "New Contact", "(800) 555-1212"}) Visual C#.NET Chapter9 ds = new Chapter9();

Chapter9.CustomersDataTable tblCustomers = ds.Customers;

tblCustomers.AddCustomersRow("ABCDE", "New Company", "New Contact", "(800) 555-1212");

//Вместо tblCustomers.Rows,Add(new object[] {"ABCDE", "New Company", "New Contact", "(BOO) 555-1212"});

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

Поиск записи В случае с обычным объектом DataSet для поиска записи по значению ее первич ного ключа можно воспользоваться методом Find набора Rows объекта DataTahle.

Иногда метод Find неудобен, особенно если для объекта DataTable определен пер вичный ключ, состоящий из нескольких полей. Например, первичный ключ таб лицы Order Details БД North wind включает поля OrderlD и ProductlD. Код поиска записи в соответствующем объекте Data-Table, использующий метод Find, будет выглядеть так:

Visual Basic.NET Dim tblDetails As DataTable Dim rowDetail As DataRow rowDetail = tblDetails.Find(New ObjectO {10245, 7}} 332 Часть III Автономная работа с данными: объект DataSet модели ADO.NET Visual C#.NET DataTable tblDetails;

DataRow rowDetail;

rowDetail = tblDetails.Find(new object[] (10245, 7}};

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

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

Visual Basic.NET Dim ds As New Chapter9() Dim tblDetails As Chapters.Order_DetailsDataTable т ds.Order_Details Dim rowDetail As Chapters.Order_DetailsRow rowDetail = tblDetails.FindByOrderIDProductID(10245, 7} If rowCustomer Is Nothing Then Console.WriteLinef'Row not found!") Else Console.WriteLineC'Found " & rowDetail.GrderlD & " - " & _ rowDetail.ProductID) End If Visual C#.NET Chapter9 ds = new Chapter9();

Chapter9.0rder_DetailsDataTable tblDetails = ds.Order_Details;

Chapter9.0rder_DetailsRow rowDetail;

rowDetail = tblDetails.FindByOrderIDProductID(10245. 7);

tf (rowDetail == null) Console.WriteLine("Row not found!");

else Console.WriteLineC'Found " + rowDetail.OrderlD.ToStringO + " + rowDetail. ProductlD.ToStringO);

Редактирование записи Редактирование записи объекта DataSet со строгим контролем типов аналогично редактированию записи обычного объекта DataSet. Вы точно так же можете при менить методы BeginEdit, EndEdit и CancelEdit. Кроме того, вы сможете обращать ся к значениям полей объекта DataRow, используя свойства объекта DataRow со строгим контролем типов:

ГЛАВА 9 Работа с объектами DataSet со строгим контролем типов Visual Basic.NET Dim ds As New Chapter9() OleDbDataAdapterl.Fill(ds) Dim rowCustomer As Chapter9.CustomersRow = ds.Customers(O) rowCustomer.CompanyName = "Modified" 'Вместо rowCustomerC'CompanyName") = "Modified" Visual C#.NET Chapter9 ds = new Chapter9();

OleDbDataAdapterl.Fill(ds);

Chapter9.CustomersRow rowCustomer = ds.Customers[0];

rowCustomer.CompanyName = "Modified";

//Вместо rowCustomer["CompanyName"] = "Modified";

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

Объекты Dataset со строгим контролем типов также упрощают работу со зна чениями NULL. Каждый объект DataRow со строгим контролем типов предостав ляет два метода: один из них проверяет, содержит ли поле значение NULL, а. дру гой позволяет задать полю такое значение. В следующем фрагменте кода исполь зуется поле ContactName, и поэтому соответствующие методы названы IsContact NameNull и SetContactNameNull.

Visual Basic.NET Dim ds As New Chapter9() OleDbDataAdapterl.Fill(ds) Dim rowCustomer As Chapter9.CustomersRow = ds.Customers(O) 'Проверяем, содержит ли поле ContactName значение Null If rowCustomer.IsContactNameNullO Then Console.WriteLinef"Contact name is Null") Else Console.WriteLine("Contact name: " & rowCustomer.ContactName) End If 'Задаем полю ContactName значение Null rowCustomer.SetContactNameNullO 'Instead of Автономная работа с данными: объект DataSet модели ADO.NET 334 Часть III If rowCustomer.IsNullC'ContactName") Then 'And rowCustomerC'ContactName") = Convert.DBNull Visual C#.NET Chapter9 ds = new Chapter9();

OleDbDataAdapterl.Fill(ds);

Chapter9,CustomersRow rowCustomer = ds.Customers[0];

//Проверяем, содержит ли поле ContactName значение Null if (rowCustomer. IsContactNameNullO) then Console.WriteLine("Contact name is Null");

else Console,WriteLine("Contact name: " + rowCustomer.ContactName);

//Задаем полю ContactName значение Null rowCustomer. SetContactNameNullO;

//Вместо if (rowCustomer.IsNull("ContactName")) //и rowCustomer["ContactName"] = Convert.DBNull;

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

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

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

Visual Basic.NET Dim ds As New Chapter9{) OleDbDataAdapterl.Fill(ds) DleDbDataAdapter2,Fill(ds) Dim rowCustomer As Chapter9.CustomersRow Dim rowOrder As Cnapter9.0rdersRow For Each rowCustomer In ds.Customers Console.WriteLineC'Orders for " & rowCustomer.CompanyName) ГЛАВА 9 Работа с объектами DataSet со строгим контролем типов For Each rowOrder In rowCustomer.GetOrdersFlowsO Console.WriteLine(vbTab & rowOrder.OrderlD & " - " & rowOrder.OrderDate) Next rowOrder Next rowCustomer 'Вместо Dim rowCustomer, rowOrder As Dataflow For Each rowCustomer In ds.Tables("Customers").Rows Console.WriteLine("Orders for " & rowCustomer("CompanyName"}) For Each rowOrder In rowCustomer.GetChildRowsC'CustomersOrders") Visual C#.NET Chapter9 ds = new Chapter9();

OleDbDataAdapterl. Fill(ds) ;

OleDbDataAdaptera. Fill(ds);

CustomersDataTable tblCustomers = ds. Customers;

CustomersRow rowCustomer = tblCustomers[0];

foreach (Chapter9. CustomersRow rowOrder in ds.Tables[ "Customers"]. Rows) { Console. WriteLine("Orders for " + rowCustomer. CompanyName);

foreach (Chapter9.0rdersRow rowOrder in rowCustomer. GetOrdersRowsO) Console. Writeline("\t" + rowOrder. OrderlD. ToStringO + " - " + rowOrder. OrderOate. ToStringO);

//Вместо foreach (DataRow rowCustomer in ds,Tables["Customers"].Rows) !

Console, WriteLine("Orders for " + rowCustomer["Co(npanyName"]>;

foreach (DataRow rowOrder in rowCustomer. GetChildRowsC'CustomersOrders")} I Прочие возможности объектов DataSet, DataTable и DataRow Классы со строгим контролем типов, генерируемые утилитой XML Schema Definition Tool, происходят от классов DataSet. DataTable и DataRow. Следовательно, с ними можно работать, как с обычными классами без контроля типов.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Visual Basic.NET 'Без контроля типов Dim dsUntyped As New DataSetO 'Создаем и заполняем DataSet txtCompanyName.Text = dsUntyped.Tables("Customers">.Rows{0)("CompanyName") 'Co строгим контролем типов Dim dsTyped As New Chapter9() 'Заполняем DataSet txtCompanyName.Text = dsTyped,Customers(0).CompanyNanie Visual C#.NET //Без контроля типов DataSet dsUntyped = new DataSetO;

//Создаем и заполняем DataSet txtCompanyName.Text = (string) dsUntyped.Tables["Customers"].Rows[0]["CompanyName"];

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

//Заполняем DataSet.

txtCompanyName.Text = dsTyped.Customers[0].CompanyName;

Производительность кода со строгим контролем типов выше. Насколько? На мо мент написания этой книги была доступна только бета-версия Visual Studio.NET, а оценивать производительность бета-версий программных продуктов всегда очень сложно. Тем не менее ряд проведенных мной тестов показал, что в показанном выше фрагменте производительность кода со строгим контролем типов почти в два раза выше производительности кода, использующего обычный объект DataSet, Работа с объектами DataSet со строгим контролем типов ГЛАВА Как же объект DataSet со строгим контролем типов обеспечивает повышенную производительность? В главе 6 мы говорили, что объекты DataRow позволяют обращаться к содержимому поля путем указания имени этого поля, порядкового номера или собственно объекта DataColumn. Код, использующий реальный объект DataColumn, обеспечивает самую высокую производительность, однако его сложнее всего писать и поддерживать. При передаче имени поля в виде строки написание и поддержка кода упрощаются, но очень сильно падает производительность, Код, генерируемый утилитой XML Schema Definition Tool, берет лучшее от обоих этих вариантов. Код, который пишете вы, удобно поддерживать, а код, генериру емый утилитой, использует объект DataColumn. На рис. 9-5 показан код, который утилита XML Schema Definition Tool генерирует, чтобы возвращать значения поля Сот panyName записей таблицы Cusmoters. Благодаря чудесам копирования и вставки на рисунке показан код, генерируемый как в Visual Basic.NET, так и в Visual Studio.NET, *х V,'iiir]t>wiiA|j|ilHMi)iin1 Mlcrovtlt Vfcujl li-itii Ml) [di' chapter 9.<* { p J Cull omeriRow (Windowiftpplli M Ic.n L tia(]ler9) Рис. 9-5- Код из класса DataSet со строгим контролем типов, обращающийся к полю CompanyName записей таблицы Customers Сравнимой производительности можно достичь, обращаясь к содержимому поля с использованием объекта DataColumn:

Visual Basic.NET Dim dsUntyped As New DataSet() 'Инициализируем и заполняем объект OataSet Dim tblCustomers As DataTable = dsUntyped.Tables(O) 'Получаем ссылку на нужное поле Dim colCompanyName As DataColumn colCompanyName = tblCustomers.Columns("CompanyName") Dim row As DataRow = tblCustomers.Rows(O) txtCompanyName.Text = CType(row(colConpanyName), String) 340 Часть III Автономная работа с данными: объект DataSet модели ADO.NET Visual C#.NET DataSet dsUntyped = new DataSetQ;

//Инициализируем и заполняем обьект DataSet DataTable tblCustomers = dsUntyped.Tables[0];

//Получаем ссылку на нужное поле DataColumn colCompanyName;

colCompanyName = tblCustomers.Columns["CompanyName"];

DataRow row = tblCustomers.Rows[0];

txtCompanyName.Text = (string) row[colCompanyName];

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

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

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

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

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

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

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

Visual Basic.NET Dim dsStrong As Mew Chapter9() Dim dsllntyped As DataSet dsUntyped = CType(dsStrong, DataSet} Visual C#.NET Chapter9 dsStrong = new Chapter9();

DataSet dsUntyped;

dsUntyped = (DataSet) dsStrong;

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

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

DataSet dsUntyped;

//Этот код выполнится успешно dsStrongl = new Chapter9();

dsUntyped = (DataSet) dsStrongl;

Автономная работа с данными: объект DataSet модели ADO.NET 342 Часть III dsStrong2 = (Chapter9) dsUntyped;

//Этот код сгенерирует исключение dsUntyped = new OataSetO;

dsStrong2 = (Chapter9) dsUntyped;

А что, если у вас есть объект DataSet без контроля типов, и вы хотите обра щаться к его содержимому с использованием класса DataSet со строгим контро лем типов? Если объект DataSet без контроля типов создавался как экземпляр обычного класса DataSet, преобразовать его в экземпляр класса DataSet со стро гим контролем типов нельзя. Тем не менее можно методом Merge класса DataSet со строгим контролем типов импортировать содержимое обычного объекта DataSet:

Visual Basic.NET Dim dsStrong As New Chapter9() Dim dsUntyped As New DataSetC) dsStrong.Merge(dsUntyped) Visual C#.NET Chapter9 dsStrong = new Chapter9();

DataSet dsUntyped = new DataSetQ;

dsSt rong.Me rge(dsUntyped);

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

Visual Basic.NET Dim dsStrongAURows As New Chapter9() 'Заполняем объект DataSet со строгим контролем типов 'и изменяем часть его записей Dim dsUntyped As DataSet dsUntyped = dsStrongAllRows.GetChangesO Dim dsStrongModifiedRows As Cnapter dsStrongHodifiedRows = CType(dsUntyped, Chapter9) Работа с объектами DataSet со строгим контролем типов ГЛАВА Visual C#.NET Chapter9 dsStrongAllRows = new Chapter9();

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

dsUntyped = dsStrongAllRows.GetChangesO;

Chapter9 dsStrongModifiedflows;

dsStrongHodifiedRows = (Chapter9) dsUntyped;

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

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

Visual Basic.NET Dim dsStrong As New Chapter9() 'Заполняем объект DataSet со строгим контролем типов 'и изменяем часть его записей Dim vueCustomers As New DataViewfdsStrong.Customers) Dim rowCustomer As Chapters.CustomersRow rowCustomer = CType(vueCustomers(Q).Row, Chapters.CustomersRow) Visual C#.NET Chapter9 dsStrong = new Chapter9();

//Заполняем объект DataSet со строгим контролем типов //и изменяем часть его записей DataView vueCustomers = new DataView(dsStrong.Customers);

Chapter9.CustomersRow rowCustomer;

rowCustomer = (Chapters.CustonersRow) vueCustomers(0).Row;

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

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

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

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

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

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

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

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

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

Работа с объектами DataSet со строгим контролем типов ГЛАВА Visual Basic.NET Dim ds As New DataSetQ ds.DataSetName = "NameForYourNewClass" Dim tbl As DataTable = ds. Tables. AddC'TableT) Dim col As DataColumn 'Задаем имя обьекта Dataflow со строгим контролем типов для объекта DataTable tbl.ExtendedProperties.AddC'typedName", "MyTablelRow") 'Задаем имя свойства DataTable объекта DataSet tbl.ExtendedProperties.Add("typedPlural", "HyTablelRows") col = tbl.Columns.AddC'StringColuflin", GetType(String)) 'Если поле содержит значение NULL, пусть класс возвращает "" col.ExtendedProperties.AddC'nullValue", "") col = tbl.Columns.Add("StringColumn2", GetType(String)) 'Если поле содержит значение NULL, пусть класс возвращает String.Empty col.ExtendedProperties.AddC'nullValue", String.Empty) col = tbl.Columns,AddC'IntegerColumn", GetType(Integer)) 'Если поле содержит значение NULL, пусть класс возвращает О col.ExtendedProperties.AddC'nullValue", "О") ds. WriteXmlScheina< "С: \Путь\К\Новону. XSD") Visual C#.NET DataSet ds = new DataSetQ;

ds.DataSetName = "NameForYourNewClass";

DataTable tbl = ds.Tables.AddCTableT);

DataColumn col;

//Задаем имя объекта Dataflow со строгим контролем типов для объекта DataTable tbl.ExtendedProperties.AddC'typedName", "MyTablelRow");

//Задаем имя свойства DataTable объекта DataSet tbl.Extended?roperties.AddC'typedPlural", "HyTablelRows");

col = tbl.Columns.AddC'StringColumn", typeof(string));

//Если поле содержит значение NULL, пусть класс возвращает "" col.ExtendedProperties.AddC'nullValue", "");

col = tbl,Columns.Add("StringColumn2", typeof(string));

//Если поле содержит значение NULL, пусть класс возвращает String.Empty col.ExtendedProperties,AddC'nullValue", String.Empty);

col = tbl.Columns.AddC'IntegerColumn", typeof(int));

//Если поле содержит значение NULL, пусть класс возвращает О col.ExtendedProperties.AddC'nullValue", "О");

ds.WriteXmlSchema<"С:\\Путь\\К\\Новому.XSD");

ГЛАВА 1О Передача обновлений в базу данных Веря в вещи, недоступные твоему пониманию.

ты страдаешь. Быть суеверным Ч неправильный образ жизни.

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

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

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

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

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

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

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

? SQL Олегу Analyzer.{Query (lof.il|itei!iBK.Morttiwjni),M Unlilleitl'J " -. 6 Vfu. g rib D 'Д.iiancicy D.DOltPl )uery batch complied Рис. 10-1. Содержимое заказа в БД Northwind Из главы 5 вы знаете, как поместить результаты запроса в объект DataSet. Ис пользуя эти знания, вы без труда создадите приложение, выбирающее заказы кли ентов в объекты DataSet. А основываясь на материалах главы 6, сумеете сделать так, чтобы приложение изменяло содержимое объекта DataSet в соответствии с инструкциями пользователя. Но, как я уже говорил, изменение содержимого DataSet не отражается на соответствующих записях БД.

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

Visual Basic.NET 'Выбираем содержимое заказа в объект DataTable Dim strConn, strSQL As String strConn = "Provider=SQLOLEDB;

Data Source=(local)\NetSDK;

" & _ "Initial Catalog=Northwind;

Trusted_Connection=Yes;

" strSQL = "SELECT OrderlD, ProductID, Quantity, UnitPrice " & _ "FROM [Order Details] WHERE OrderlD = 10503 " & _ "ORDER BY ProductID" Dim da As New QleDbDataAdapter(strSQL, strConn) Dim tbl As New DataTable("Order Details") 348 Часть III Автономная работа с данными: объект DataSet модели ADO.NET da.Fill(tbl) 'Изменяем содержимое заказа tbl.Rows(0).Delete() tbl.Rows(1)("Ouantity") = CShort(tbl.Rows(1)("Quantity")) * tbl.Rows.Add(New ObjectO {10503, 1, 24, 16}} 'Передаем отложенные изменения Try da.Update(tbl) Console.WriteLine("Successfully submitted new changes") Catch ex As Exception Console.WriteLine("Call to DataAdapter.Update " & _ "threw exception:" & vbCrLf & ex.Message) End Try Visual C#.NET //Выбираем содержимое заказа в объект OataTable string strConn, strSOL;

strConn = "Provider=SQLOLEDB;

Data Source=(local)\\NetSDK;

" + "Initial Catalog=Northwind;

Trusted_Connection=Yes;

";

strSQL = "SELECT OrderlD, ProductID, Quantity, UnitPrice " + "FROM [Order Details] WHERE OrderlD = 10503 " + "ORDER BY ProductID";

OleDbDataAdapter da = new 01eDbDataAdapter(strSQL, strConn);

DataTable tbl = new DataTableC'Order Details");

da.Fill(tbl);

//Изменяем содержимое заказа tbl.Rows[0],Delete();

tbl.Rows[1]["Quantity"] = (short) (tbl.Rows[1]["Quantity"]) - 2;

tbl.Rows.Addfnew object[] {10503, 1, 24, 18});

//Передаем отложенные изменения try I da.Update{tbl);

Console.WriteLineC'Successfully submitted new changes");

I catch (Exception ex) ;

Console.WriteLine("Call to DataAdapter.Update threw exception:\n" + ex.Message);

> Данный код успешно компилируется, но не способен успешно передать в БД изменения в составе заказа. Вместо передачи изменений ADO.NET сгенерирует ис ключение, гласящее: Update requires a valid DeleteCommand when passed DataRow collection with deleted rows* (Методу Update, принявшему набор DataRow с уда ленными записями, необходим корректный объект DeleteCommand).

Передача обновлений в базу данных ГЛАВА На этапе тестирования бета-версии Microsoft.NET Framework такие исключе ния путали многих разработчиков. Предыдущие технологии доступа к данным, например ADO, включали функции, позволявшие автоматически передавать из менения. В ADO.NET передавать изменения разрешается средствами объекта Data Adapter, однако по умолчанию он не содержит необходимой для этого логики.

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

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

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

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

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

Классический Visual Basic и ADO 2.x Dim strConn As String, strSQL As String strConn = "Provider=SQLOLEDB;

Data Source=(local)\NetSDK;

" & _ "Initial Catalog=Northwind;

Trusted_Connection=Yes;

" strSQL = "SELECT OrderlD, ProductID, Quantity, UnitPrice " & _ "FROM [Order Details] WHERE OrderlD = Ю503 " & _ "ORDER BY ProductID" Dim rs As ADODB.Recordset Set rs = New ADODB.Recordset rs.Cursor-Location = adUseClient rs.Open strSQL, strConn, adOpenStatic, adLockBatchOptimistic, adCmdText rs.Delete rs.MoveNext * rs.Fields("Quantity") = 2 - rs.Fields("Quantity") rs.Update Автономная работа с данными: объект DataSet модели ADO.NET 350 Часть rs.AddNew rs.Fields{"OrderID") = rs.Fields("ProductID") = rs.FieldsC"Quantity") = rs.FieldsC'UnitPrice") = rs.Update rs.UpdateBatch rs.Close en.Close Данный код демонстрирует большинство преимуществ и недостатков переда чи обновлений в БД с использованием объекта Recordset ADO, и я подробно рас скажу об этом в следующих разделах этой главы.

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

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

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

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

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

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

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

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

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

With rs.Fieldsf'Quantity") Debug.Print "BaseTableName = " &.Properties("BaseTableName") Debug,Print "BaseColumnName = " &,PropertiesC'BaseColumnName") Debug.Print "KeyColumn = " &.Properties("KeyColumn") End With Здесь проявляется первый значительный недостаток функций обновления, предоставляемых ядром курсоров ADO, Ч производительность. Запросы, генери руемые ядром для получения из БД сведений о таблицах, столбцах и первичных ключах, сильно снижают производительность. Большинству разработчиков, созда ющих код для доступа к данным, известно происхождение этих данных. К сожа лению, в ADO нельзя предоставить такие данные в коде и исключить необходи мость получать их из БД каждый раз при открытии объекта Recordset.

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

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

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

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

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

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

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

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

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

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

Visual Basic.NET Private Function CreateUpdateCommandO As OleDbCommand Dim strSQL As String strSQL = "UPDATE [Order Details] " & _ SET OrderlD = ?, ProductID = ?, " & _ Quantity = ?, UnitPrice = ? " & _ WHERE OrderlD = ? AND ProductID = ? AND Quantity = ? AND UnitPrice = ?" Dim cmd As New QleDbCommand(strSQL, en) Dim pc As OleDbParameterCollection = cmd.Parameters F)c.Add("OrderID_New", OleDbType.Integer) pc.Add("ProductID Jiew", OleDbType.Integer) pc. Add(" Quant ityjlew", OleDbType. SmaUInt) ГЛАВА 10 Передача обновлений в базу данных pc.Add("UnitPrice_New", OleDbType.Currency) pc.Add("OrderID_Orig", OleDbType.Integer) pc.AddC"ProductID_Orig", QleDbType.Integer) pc.Add("Quantity_Orig", OleDbType.Smalllnt) pc.Add("UnitPrice_Orig", OleDbType.Currency) Return cmd End Function Private Function CreatelnsertCommandQ As OleDbCommand Dim strSQL As String strSQL = "INSERT INTO [Order Details] " & _ (OrderlD, ProductID, Quantity, UnitPrice) " VALUES (?, ?, ?, ?)" Dim cmd As New OleDbCommandfstrSQL, en) Dim pc As OleDbParameterCollection = end.Parameters pc.Add("OrderlD", OleDbType.Integer) pc.Add("ProductID", OleDbType.Integer) pc.Add("Quantity", OleDbType.Smalllnt) pc.Add("UnitPrice", OleDbType.Currency) Return cmd End Function Private Function CreateDeleteComrnandQ As OleDbCommand Dim strSQL As String strSQL = "DELETE FROM [Order Details] " & _ WHERE OrderlD = ? AND ProductID = ? AND ' Quantity = ? AND UnitPrice = ?" Dim cmd As New OleDbCornmandCstrSQL, en) Dim pc As OleDbParameterCollection = cmd.Parameters pc.Add("OrderlD", OleDbType.Integer) pc.Add("ProductID", OleDbType.Integer) pc.Add("Quantity", OleDbType.Smalllnt) pc.Add("UnitPrice", OleDbType,Currency) Return cmd End Function Visual C#.NET static OleDbCommand CreateUpdateCommandQ { string strSQL;

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

OleDbCommand cmd = new 01eDbCommand(strSQL, en);

OleDbParameterCollection pc = cmd.Parameters;

pc.Add("OrderID_New", OleDbType.Integer);

pc.Add("ProductID_New", OleDbType,Integer);

pc.Add("Quantity_New", OleDbType.Smalllnt);

pc.Add("UnitPrice_New", OleDbType.Currency);

pc.Add("QrderID_Orig", OleDbType.Integer);

pc.Add("ProductID_Orig", OleDbType.Integer);

pc.Add("Quantity_Orig", OleDbType.Smalllnt);

pc.Add("UnitPrice_Orig", OleDbType.Currency);

return cmd;

i static OleDbCommand CreatelnsertCommandO string strSQL;

strSQL = "INSERT INTO [Order Details] " + (OrderlD, ProductID, Quantity, UnitPrtce) " + VALUES (?, ?, ?, ?)";

OleDbCommand cmd = new 01eDbCommand(strSQL, en);

OleDbParameterCollection pc = cmd.Parameters;

pc.Addf"OrderlD", OleDbType.Integer);

pc.Add("ProductID", OleDbType.Integer);

pc.AddC"Quantity", OleDbType.Sfnalllnt);

pc.Add("UnitPrice", OleDbType,Currency);

return emu;

' static OleDbCommand CreateDeleteCommandO < string strSQL;

StrSQL = "DELETE FROM [Order Details] " + WHERE OrderlD = ? AND ProductID = ? AND " + Quantity = ? AND UnitPrice = ?";

OleDbCommand cmd = new 01eDbCommand(strSQL, en);

OleDbParameterCollection pc = cmd.Parameters;

pc,Add("OrderlD", OleDbType.Integer);

pc.Add("ProductID", OleDbType.Integer);

pc.Add("Quantity", OleDbType.Smalllnt);

pc.Add("UnitPrice", OleDbType.Currency);

return cmd;

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

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

Visual Basic.NET Private Sub SubmitChangesByHandO Dim cmdUpdate As OleDbCommand = CreateUpdateCommandO Dim cmdlnsert As OleDbConmand = CreatelnsertCommandO Dim cmdDelete As OleDbCommand = CreateDeleteCommandO Dim row As Dataflow Dim IntRowsAffected As Integer Dim dvrs As DataViewRowState dvrs = DataViewHowState.ModifiedCurrent Or DataViewRowState.Deleted Or DataViewRowState.Added For Each row In tbl.Select{"", "", dvrs) Select Case row.RowState Case DataRowState.Modified intRowsAffected = SubmitUpdate(row, cmdUpdate) Case DataRowState.Added intRowsAffected = Submitlnsert(row, cmdlnsert) Case DataRowState.Deleted intRowsAffected = SubmitDelete{row, cmdDelete) End Select If intRowsAffected = 1 Then row.AcceptChangesO Else row.RowError = "Update attempt failed" End If Next row End Sub Private Function SubmitUpdate(ByVal row As Dataflow, _ ByVal cmd As OleDbCommand) As Integer Dim pc As OleDbParameterCollection = cmd.Parameters pc("OrderID_New").Value = row("OrderID") pc("ProductID_New").Value = row("ProductID") pc("Quantity_New").Value = row("Quantity") pc("UnitPrlce_New").Value = rowC'UnitPrice") pc("OrderID_Orig").Value = row("OrderID", _ DataRowVersion.Original) pc("Quantity_Qrig"}.Value = row("Quantity", _ DataRowVersion.Original) 356 Часть III Автономная работа с данными: объект DataSet модели ADO.NET pc("ProductID_Orig").Value = row("ProductID", _ DataRowVersion.Original) pc("UnitPrice_Orig").Value = row("UnitPrice", DataRowVersion.Original) Return cmd.ExecuteNonQuery End Function Private Function SubmitInsert(8yVal row As DataRow, _ ByVal cmd As OleDbCommand) As Integer Dim pc As OleDbParameterCollection = cmd.Parameters pcf'OrderlD").Value = row("OrderID") pcC'ProductID").Value = row("ProductID") pc("Quantity").Value = row("Quantity") pcC'UnitPrice").Value = row("UnitPrice") Return cmd.ExecuteNonQuery End Function Private Function SubmitDelete(ByVal row As OataRow, ByVal cmd As OleDbCommand) As Integer Dim pc As OleDbParameterCollection = cmd.Parameters pc{"OrderID").Value = row("OrderID", DataRowVersion.Original) pcC'ProductID").Value = row("Product10", DataRowVersion.Original) pc("Quantity").Value = row("Quantity", DataRowVersion.Original) pc("UnitPrice").Value = row("UnitPrice", DataRowVersion.Original) Return cmd.ExecuteNonQuery End Function Visual C#.NET static void SubmitChangesByHand() < OleDbCommand cmdUpdate = CreateUpdateCommandO;

OleDbCommand cmdlnsert = CreatelnsertComrnandO;

OleDbCommand cmdDelete = CreateDeLeteCommandQ;

DataViewRowState dvrs;

dvrs = DataViewRowState.ModifiedCurrent | DataViewRowState.Deleted | DataViewRowState.Added;

int intflowsAffected = 0;

foreach {Dataflow row in tbl.Select("", "", dvrs)) { switch (row.RowState) !

case DataRowState.Modified:

intRowsAffected = SubmitUpdate{row, cmdUpdate);

break;

case DataRowState.Added:

intRowsAffected = Submitlnsert{row, cmdlnsert);

break;

case DataRowState.Deleted:

intRowsAffected = SubmitDelete(row, cmdDelete);

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

} if (irrtRowsAffected == 1) row. AcceptChangesQ ;

else row. RowError = "Update attempt failed";

static int SubmitUpdate(DataRow row, 01 subcommand cmd) { OleDbParameterCollection pc = cmd. Parameters;

pc["OrderID_New"]. Value = row["OrderID"];

pc["ProductID_New"]. Value = row["ProductID"];

pc["Quantity_New"]. Value = row["Quantity"];

pc["UnitPrice_New"]. Value = row["UnitPrice"];

pct"OrderID_Orig"]. Value = row["OrderID", DataRowVersion. Original];

pc["ProductID_Orig"]. Value = row["ProductID", DataRowVersion. Original];

pc["Quantity_Orig"]. Value = row["0uantity", DataRowVersion. Original];

pc["UnitPrice_Orig"]. Value = row["UnitPrice", DataRowVersion. Original];

return cfnd.ExecuteNonQueryO;

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

pc["OrderIO"]. Value = row["Order!D"];

pc["Product!D"]. Value = row["ProductID"];

pc[Quantity"]. Value = row["Quantity"];

pc["UnitPrice"]. Value = row["UnitPrice"];

return cmd. ExecuteNonQueryO;

static int SubmitDeletefDataRow row, OleDbCommand cmd) { OleDbParameterCollection pc = cmd. Parameters;

pc["Order!D"]. Value = row["OrderID", DataRowVersion. Original];

pc["ProductID"]. Value = row["ProductID", DataRowVe rsion. 0 riginal ] ;

pc["Quantity"]. Value = row[ "Quantity", DataRowVe rsion. Original];

pc["UnitPrice"]. Value = row["UnitPrice", DataRowVersion, Original];

return cmd. ExecuteNonQueryO;

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

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

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

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

Visual Basic.NET Dim en As OleDbConnection Dim da As OleDbDataAdapter Dim tbl As DataTable = GenTableO Sub Main() Dim strConn, strSQL As String strConn = "Provider=SQLOLEDB;

Data Source=(local)\NetSDK;

" 4 _ "Initial Catalog=Northwind;

Trusted_Connection=Yes;

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