
David Sceppa Microsoft' ADO.NET Microsoft Press Дэвид Сеппа Microsoft ADO.NET ...
-- [ Страница 4 ] --Visual Basic.NET Dim da As OleDbDataAdapter Dim ds As DataSet Dim intChangesSubmitted As Integer intChangesSubmitted = da.Update(ds) intChangesSubmitted = da.Update(ds, "TableName") intChangesSubmitted = da.Update(ds.Tables("TableName")) Dim aflowsf) As Dataflow intChangesSubmitted = da.Update(aRows) Visual C#.NET OleDbDataAdapter da;
DataSet ds;
irtt intChangesSubmitted;
intChangesSubmitted = da.Update(ds);
intChangesSubmitted = da.Update(ds, "TableName"};
intChangesSubmitted = da.Update(ds.Tables["TableName"]);
DataRow[] aRows;
intChangesSubmitted = da.Update(aRows);
События объекта DataAdapter В табл. 5-3 перечислены три события, предоставляемых объектом DataAdapter.
Таблица 5-3. События объекта OleDbDataAdapter Событие Описание Наступает, если при заполнении объекта DataSet или DataTable объект Ь'ШЕггог DataAdapter столкнулся с какой-либо ошибкой Наступает перед передачей измененной записи в БД RoivUpdating Наступает после передачи измененной записи в БД RowUpdated Событие FillError Если при заполнении объекта DataSet или DataTable объект DataAdapter столкнулся с ошибкой, ее можно перехватить при помощи события FillError.
Получение данных с помощью объектов DataAdapter ГЛАВА Visual Basic.NET Dim strConn, strSQL As String strConn = "Provider=SQLOLEDB;
Data Source=(local)\NetSDK;
" & "Initial Catalog=Northwind;
Trusted_Connection=Yes;
" strSQL = "SELECT TOP 1 OrderlD, CustomerlD, EmployeelO FROM Orders" Dim da As New 01eDbDataAdapter(strSQL, strConn) da.MissingSchemaAction = MissingSchemaAction. Error AddHandler da.FillError, AddressOf da_FillError Dim tbl As New DataTable("Orders") tbl.Columns.Add("OrderlD", GetType(Integer)) tbl.Columns.Add("CustomerlD", GetType(String)) da.Fill(tbl) Public Sub da_FillError(ByVal sender As Object, ByVal e As FillErrorEventArgs) Console.WriteLine(e.Errors.Message) e.Continue = True End Sub Visual C#.NET string strConn, strSQL;
strConn = "Provider=SQLOLEDB;
Data Source=(local)\\NetSDK;
" + "Initial Catalog=Northwind;
Trusted_Connection=Yes;
";
strSOL = "SELECT TOP 1 OrderlD, CustomerlD, EmployeelD FROM Orders";
OleDbDataAdapter da = new 01eDbDataAdapter(strSQL, strConn);
da,MissingSchemaAction = MissingSchemaAction.Error;
da.FillError += new FillErrorEventHandler(da_FillError);
DataTable tbl = new DataTableC'Orders");
tbl.Columns.Add("OrderlD", typeof(int));
tbl.Columns.Add("CustomerID", typeof(st ring));
da.Fill(tbl);
static void da_FillError(object sender, FillErrorEventArgs e) { Console,WriteLine(e.Errors.Message);
e.Continue = true;
} Отмечу, что с помощью события FillError нельзя перехватывать ошибки, воз никающие, если полученные объектом DataAdapter данные нарушают ограниче ния объекта DataSet или DataTable.
События RowUpdating и RowUpdated Объект DataAdapter также генерирует события при передаче в БД отложенных изменений с помощью метода DataAdapter.Update. Если вам нужно просмотреть отложенные изменения перед тем, как передать их в БД, воспользуйтесь событи ем RowUpdating. Чтобы сразу после передачи изменений в БД выполнить код, за дайте событие RowUpdatea.
Следующий фрагмент кода демонстрирует, как использовать оба этих события.
7- Часть II Подключаемся: использование поставщика данных.NET 1 Visual Basic.NET Dint strConn, strSQL As String strConn = "Provider=SQLOLEOB;
Data Source=(local)\NetSDK;
" & _ "Initial Catalog=Northwind;
Trusted_Connection=Yes;
" strSQL = "SELECT TOP 1 OrderlD, CustomerlD, EmployeelD FROM Orders" Dim da As New 01eDbDataAdapter(strSQL, strConn) AddHandler da.RowUpdated, AddressOf da_RowUpdated AddHandler da.RowUpdating, AddressOf da_RowUpdating Dim tbl As New DataTablef'Orders") da.Fill(tbl) tbl.Rows(0)("EmployeeID") = CInt(tbl.Rows(0)("EmployeeID")) + Dim cb As New OleDbCommandBuilder(da) da.Update(tbl) tbl.Rows(0)("EmployeeID") = CInt(tbl.Rows(0)("EmployeeID")) - da.Update(tbl) Public Sub da_RowL)pdating(ByVal sender As Object, ByVal e As OleDbRowUpdatingEventArgs) Console.WriteLine("RowUpdating Event: " & e.StatementType.ToString) Console.WriteLine(vbTab & "OrderlD = " & e.Row{"OrderID")> Console.WriteLinefvbTab & "EmployeelD from: " & e.Row{"EfnplcyeeID", DataRowVersion.Original Console.WriteLine(vbTab & "EmployeelD to : " & e.Row("EmployeeID")) Console.WriteLineO End Sub Public Sub da_RowUpdated(ByVal sender As Object, ByVal e As OleDbRowUpdatedEventArgs) Console.WriteLineC"RowUpdated Event: " & e.StatementType.ToString) Console.WriteLine(vbTab & "OrderlD = " & e.Row("OrderID")) If e.Status = UpdateStatus.ErrorsOccurred Then Console.WriteLine(vbTab & "Errors occurred") Else Console.WriteLine(vbTab & "Success!") End If Console.WriteLineO End Sub Visual C#.NET string strConn, strSQL;
strConn = "Provider=SQLOLEDB;
Data Source=(local)\\NetSDK;
" + "Initial Catalog=Northwind;
Trusted_Connection=Yes;
";
StrSQL = "SELECT TOP 1 OrderlD, CustomerlD, EmployeelD FROM Orders";
OleDbDataAdapter da = new OleDbDataAdapterfstrSQL, strConn);
da.RowUpdated += new 01eDbRowUpdatedEventHandler(da_RowUpdated);
da.RowUpdating += new 01eDbflowUpdatingEventHandler(da_Rowllpdating);
DataTable tbl = new DataTable("Orders");
da.Fill(tbl);
tbl.Rows[0]["EmployeeID"] = (int) tbl.Rows[0]["EmployeeID"] + 1;
ГЛАВА 5 Получение данных с помощью объектов DataAdapter OleDbCommandBuilder cb = new OleDbCommandBuilder(da);
da.Update(tbl);
tbl.Rows[0]["EmployeeID"] = (int) tbl.Rows[0]["EmployeeID"] - 1;
da.Update(tbl);
static void da_RowUpdating(object sender, OleDbRowUpdatingEventArgs e) { Console. WriteLine("RowUpdating Event: " + e.StateraentType.ToStringO);
Console, WriteLine( "\tOrderID = " + e.Row["OrderID"]);
Console. WriteLine("\tEmployeeID from: " + e. Row["EmployeeID", OataRowVersion. Original]);
Console. WriteLine("\tEmployeeID to : " + e.Row["EmployeeID"]);
Console. WriteLineC);
static void da_RowUpdated(object sender, OleDbRowUpdatedEventArgs e) { Console. WriteLineC "RowUpdated Event: " + e.StatementType.ToStringO);
Console. WriteLine<"\tOrderID = " + e.Row["OrderID"]);
if (e. Status == UpdateStatus. ErrorsOccurred) Console. WriteLine{ "\tErrors осей г red" ) ;
else Console. Writ eLine{ "\tSuccess! ");
Console. WriteLineC ) ;
Вопросы, которые стоит задавать почаще Вопрос. Какой способ создания объектов DataTable, по-вашему, самый лучший:
Х создание объектов DataTable в коде перед заполнением их при помощи объекта DataAdapter, Х явное создание объектов DataTable посредством метода DataAdaplterfill, Х создание объектов DataTable при помощи метода DataAdapterFillSchemat Ответ. Настоятельно рекомендую вам первый способ. Когда я работал над этой книгой, на создание объектов DataTable в коде требовалось приблизительно в 20 раз меньше времени, чем на создание их средствами метода FillSchema объекта Data Adapter.
Вопрос. Как вы и рекомендовали, я создал объекты DataTable программно, од нако теперь код, заполняющий эти объекты, выполняется гораздо медленнее. В чем же дело?
Ответ. Я рад слышать этот вопрос. Производительность снижается, если на объек тах DataTable определены ограничения. Когда вы получаете данные из хранили ща и добавляете записи в объекты DataTable, ADO. NET проверяет каждую новую запись на соответствие имеющимся ограничениям. Кроме того, ограничения, например PRIMARY KEY или UNIQUE, требуют, чтобы ADO.NET дополнительно просматривала объект DataTable и убеждалась, что создаваемая новая запись не Часть II Подключаемся: использование поставщика данных.NET нарушает эти ограничения. Таким образом, при добавлении дополнительных за писей в объект DataTable производительность снижается еще сильнее.
В целом, создаваемые в объектах DataTable ограничения имеются и в БД. Если предположить, что это так, то БД уже проверила данные, которые вы собираетесь вставить в объекты DataTable. Проверять их заново нет смысла. Есть ли какой-то способ, который позволяет определять ограничения на объектах DataTable и ис ключает падение производительности, связанное с проверкой данных, получае мых при помощи метода DataAdapterf'ilP.
Можно создать объекты DataTable без'ограничений, затем заполнить их при помощи метода DataAdapterfill и только после этого добавить необходимые ог раничения Ч это самое элегантное решение данной проблемы.
Тем не менее команда разработчиков ADO.NET предусмотрела такую ситуацию и предлагает вам не менее изящное решение. У класса DataSet есть свойство Enforce Constraints, значение по умолчанию которого Ч True. Это означает, что ADO.NET гарантирует соблюдение ограничений в объекте DataSet. Однако можно непо средственно перед вызовом метода Fill объектов DataAdapter задать этому свой ству значение False, а по завершении Ч исходное значение, True:
Visual Basic.NET ds.EnforceConstraints = False dat. FilKds.Tables("Tablel")) da2.Fill(ds.Tables("Table2")) ds.EnforceConstraints = True Visual C#.NET ds.EnforceConstraints = false;
da1.Fill(ds.Tables["Tablel"]);
da2.Fill(ds.Tables["Table2"]);
ds.EnforceConstraints = true;
Теперь метод Fill будет получать данные так же быстро, как и в случае, когда на объектах DataTable не определены какие-либо ограничения.
ЧАСТЬ АВТОНОМНАЯ РАБОТА СДАННЫМИ:
ОБЪЕКТ DATASET МОДЕЛИ ADO.NET ГЛАВА Работа с объектами DataSet 11редыдущие три главы посвящены базовой функциональности соединенных классов объектной модели ADO.NET, формирующих поставщика данных.NET, Теперь мы обсудим отсоединенную половину Ч классы, с помощью которых ADO.NET предоставляет многофункциональный, реляционный, отсоединенный кэш данных. Я расскажу об основах хранения данных в классе DataSet, а также в дру гих классах, входящих в состав объекта DataSet Возможности объекта DataSet По сути, объект DataSet Ч это набор данных. Обычно разработчики представля ют результаты, возвращаемые запросом, при помощи сетки, во многом напоми нающей электронную таблицу Microsoft Excel. Объект DataSet годится для хране ния результатов отдельного запроса, но его скорее следует сравнивать с книгой Excel, поскольку в нем можно разместить результаты нескольких запросов.
Модель ADO.NET уже включает средство просмотра результатов запроса объект DataReader. Зачем же нужен еще один объект?
Как говорилось в главе 4, DataReader Ч быстрая и эффективная структура, по зволяющая получать результаты запроса. Основное назначение объекта DataRea der Ч обеспечить высокую скорость работы, и поэтому его функциональность ог раничена. Данные в объекте DataReader доступны только для чтения, и после пе рехода к следующей записи нет какой-либо возможности вернуться и просмот реть предыдущие записи.
Объект DataSet предоставляет гораздо более широкую функциональность. Рас смотрим некоторые его возможности.
ГЛАВА 6 Работа с объектами DataSet Работа с отсоединенными данными Данные в объекте DataSet отсоединены от БД. После того как вы выберете резуль таты запроса в объект DataSet с помощью объекта DataAdapter, соединение меж ду БД и объектом DataSet перестает существовать. Изменения содержимого объеЕСта DataSet не сказываются на содержимом БД. Если другие пользователи изменят данные БД, соответствующие содержимому вашего объекта DataSet, вы этих из менений не увидите.
Определенно, у работы с отсоединенными структурами данных есть преиму щества. Первое Ч вам не требуется живое соединение с БД. Выбрав результаты запроса в объект DataSet, можно закрыть соединение с БД и продолжить работать с данными в объекте.
Отсоединенные структуры данных типа DataSet также полезны при создании многоуровневых приложений. Если ваше приложение обращается к БД с помо щью бизнес-объектов, выполняющихся на промежуточном уровне, бизнес-объектам придется передавать клиентскому приложению отсоединенные структуры данных.
Объект DataSet предназначен как раз для таких ситуаций. Его содержимое можно передать от одного компонента другому. Компонент, принимающий данные, спо собен работать с ними, как с объектом DataSet (если компонент создан на основе Microsoft.NET Framework) или как с XML-документом.
Прокрутка, сортировка, поиск и фильтрация Объект DataSet позволяет в любое время просмотреть содержимое любой своей записи. Вы вправе перемещаться по результатам запроса взад и вперед столько, сколько захотите. Благодаря этому, DataSet идеально подходит для ситуаций, ког да коду требуется циклично перемещаться по данным (например, при создании отчетов). Кроме того, удается легко создать приложение, позволяющее перемещать ся взад-вперед по результатам запроса.
Объект DataSet также позволяет сменить способ просмотра результатов запроса, Данные в объекте DataSet разрешено сортировать по отдельному полю или груп пе полей. Можно искать запись данных по простому критерию поиска, а также определить на содержимом объекта DataSet фильтр, чтобы отображались только записи, удовлетворяющие заданным критериям. Подробнее об этом Ч в главе 8, Работа с иерархически организованными данными Объекты DataSet предназначены для работы с иерархически организованны \т данными. В главе 2 мы с помощью мастера Data Form Wizard создали простое приложение Microsoft Windows, получавшее информацию из двух таблиц Ч Custo mers и Orders. Форма, созданная мастером, обеспечивает перемещение по запи сям о клиентах, и когда вы переходите от одного клиента к другому, форма ото бражает список размещенных только этим клиентом заказов.
Объект DataSet позволяет определить отношения между хранящимися в нем таблицами данных. Мастер Data Form Wizard создал похожее отношение на ос нове вашего ввода и затем связал с этим отношением объект DataGrid, чтобы ото бражались только заказы текущего клиента (подробнее об объекте DataRelation Ч в следующей главе).
Часть 111 Автономная работа с данными: объект DataSet модели ADO.NET 1 Кэширование изменений Работать с данными, доступными только для чтения, очень просто. Одна из глав нейших проблем при создании приложения для работы с БД Ч преобразование ввода пользователя в изменения содержимого вашей БД. Еще большая проблема Ч встроить подобную логик}' в многоуровневое приложение, которому требуется кэшировать изменения, и затем передавать их все сразу в БД.
'Объект DataSet позволяет кэшировать изменения записи данных и затем пе редавать эти изменения в БД при помощи объекта DataAdapter. Кроме того, мож но просматривать измененные записи объекта DataSet и определять, как именно они изменены (вставлены, отредактированы или удалены), а также сравнивать оригинальное и текущее содержимое каждой записи.
В этой главе рассказывается, как изменять содержимое объекта DataSet. Подробнее о передаче отложенных изменений в БД с помощью объекта DataAdapter Ч в гла вах 10 и 11.
Интеграция с XML Объект DataSet модели ADO.NET изначально рассчитан на работу с XML. Содер жимое DataSet можно загружать и сохранять в виде XML-документов. Кроме того, DataSet позволяет выделить информацию схемы (сведения о таблицах, столбцах и ограничениях) в файл XML-схемы, В ADO.NET объекты DataSet и XML-документы практически взаимозаменяемы.
Переходить от одной структуры данных к другой очень просто. Благодаря такой дуалистичности, разработчики имеют возможность выбирать наиболее удобные интерфейсы. XML-программисты могут работать с объектами DataSet, как с XML документами, а программисты БД Ч с XML-документами, как с объектами DataSet.
Подробнее об XML-функциях объекта DataSet Ч в главе 12.
Универсальная функциональность Разработчики, имеющие опыт работы с ADO, вероятно, знают, что возможности объекта Recordset аналогичны возможностям объекта DataSet. Например, Recordset поддерживает фильтрацию, поиск, сортировку и кэширование обновлений. Тем не менее функциональность объекта Recordset во многом определяется тем, как он открыт.
Например, если не изменять параметры по умолчанию объектов Recordset и Connection модели ADO, узнать точное число записей в объекте Recordset невоз можно. У объекта Recordset есть метод Supports, который разработчики часто ис пользуют, чтобы узнать, какая же функциональность доступна. Можно ли изме нить содержимое Recordset? Если обновить запись, передаст ли Recordset измене ние в БД сразу или кэширует его? Можно ли связать Recordset с сеткой? Можно ли перейти к предыдущей записи?
Причина, по которой не все объекты Recordset предоставляют одинаковую функциональность, Ч то, что Recordset пытается быть всем для всех. Работаете ли вы в ADO с пожарным курсором, серверным курсором или отсоединенными дан ными, вы использ^'ете объект Recordset.
ГЛАВА 6 Работа с объектами DataSet Объект DataSet модели ADO.NET не требует такой интеграции, поскольку пред назначен именно для отсоединенных данных. Как следствие, ADO.NET-разработ чики никогда не шлют на известный адрес электронной почты сообщений типа Почему значение RecordsCount для моего объекта Recordset равно -!?Х> или Как понимать The rowset is not bookmarkable?.
Использование объектов DataSet DataSet и его дочерние объекты в чем-то напоминают вложенных друг в друга матрешек. Объект DataSet содержит объекты DataTable и DataRelation. Объект DataTable содержит объекты DataRow, DataColumn и Constraint.
Вместо рассказа об использовании каждого объекта по отдельности, я на про стых примерах объясню базовую функциональность объекта DataSet. А попутно и обо всех вышеперечисленных объектах.
Создание объекта DataSet Создать экземпляр объекта DataSet в коде весьма просто Ч достаточно восполь зоваться ключевым словом New языка по вашему выбору. У объекта DataSet есть дополнительный конструктор, позволяющий задать значение свойства DataSetName этого объекта.
Visual Basic.NET Dim ds As New DataSetC'DataSetName") Console.WriteLine(ds.DataSetName) Visual C#.NET DataSet ds = new DataSetC'DataSetName");
Console.WriteLine(ds.DataSetName);
Класс DataSet и классы, содержащиеся в объектах DataSet, Ч DataTable, Data Column, DataRow, Constraint и DataRelation Ч относятся к пространству имен SystemData, Просмотр структуры, создаваемой при вызове метода Data Adapter.Fill В главе 5 рассказывалось, как поместить результаты запроса в объект DataSet с помощью метода Fill объекта DataAdapter.
Visual Basic.NET Dim strConn, strSQL As String strConn = "Provider=SQLOLEDB;
Data Source=(local)\NetSDK;
" & _ "Initial Catalog=Northwind;
Trusted_Connection=Yes;
" strSQL = "SELECT CustomerlD, CompanyName, ContactName, Phone " & _ "FROM Customers" Dim da As New 01eDbDataAdapter(strSQL, strConn) Dim ds As New DataSetQ da.Fillfds, "Customers") Часть III Автономная работа с данными: обьект DataSet модели ADO.NET 1 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 " + "FROM Customers";
OleDbDataAdapter da = new 01eDbDataAdapter{strSQL, strConn);
DataSet ds = new DataSetQ;
da.Fill(ds, "Customers");
Прежде чем просмотреть результаты запроса, я расскажу о структуре, создан ной объектом DataAdapter для хранения этих результатов.
Объект DataTable DataAdapter помещает результаты запроса в объект DataTable. аналогичный обсуж давшемуся в главе 4 объекту DataReadeг. Просмотреть результаты запроса можно посредством любого из этих объектов. Оба они предоставляют результаты в виде набора записей и столбцов.
Вы, вероятно, помните, что DataReader заточен на производительность. Он позволяет быстро промчаться по результатам запроса и предоставляет очень мало дополнительной функциональности. Как вы знаете, изменить данные объекта DataReader или вернуться к предыдущей записи нельзя. Объект DataTable рассчитан на хранение данных длительного пользования и предоставляет больше функци ональности, чем DataReader. Он позволяет редактировать, сортировать и фильт ровать данные;
в случае с DataReader это невозможно.
Дад работы с этими данными длительного пользования объект DataTable пре доставляет соответствующую структуру. Свойство Columns объекта DataTable воз вращает набор объектов DataColumn, каждый из которых соответствует столбцу результатов вашего запроса.
Такая структура будет знакома программистам, имеющим опыт работы с DAO и ADO, поскольку у объекта Recordset есть свойство Fields, возвращающее набор объектов Field.
Объект DataColumn Если вкратце, объекты DataColumn определяют схему вашего объекта DataTable.
Когда вы с помощью метода DataAdapterfill создаете новый объект DataTable, DataAdapter также создает объекты DataColumn, соответствующие столбцам на бора результатов. У этих новых объектов DataColumn заданы только самые основ ные свойства Ч Name, Ordinal и DataType, Следующий фрагмент кода выводит базовую информацию об объектах Data Column, создаваемых при вызове метода DataAdapter fill, Visual Basic.NET Dim strConn, strSQL As String strConn = "Provider=SQLOLEDB;
Data Source=(local)\NetSDK;
" & "Initial Catalog=Northwind;
Trusted_Connection=Yes;
" strSQL = "SELECT OrderlD, CustomerlD, EmployeelD, OrderDate " & "FROM Orders" Работа с объектами DataSel ГЛАВА Dim da As New 01eDbDataAdapter(strSQL, strConn) Dim ds As New DataSetQ da.FilKds, "Orders") Dim tbl As DataTable = ds.Tables(O) Console.WriteLineC'Column information for " & tbl.TableName & " DataTable") Dim col As DataColumn For Each col In tbl.Columns Console.WriteLine(vbTab & col.ColumnName & " - " & _ col.DataType.ToString) Next col Visual C#.NET string strConn, strSQL;
strConn = "Provider=SQLOLEDB;
Data Source=(local)\\NetSDK;
" + "Initial Catalog=Northwind;
Trusted_Connection=Yes;
";
strSQL = "SELECT OrderlD, CustomerlD, EmployeelD, OrderDate " + "FROK Orders";
OleDbDataAdapter da = new 01eDbDataAdapter(strSQL, strConn);
DataSet ds = new DataSetQ;
da.FilKds, "Orders");
DataTable tbl = ds.Tables[0];
Console.WriteLineC'Column information for " + tbl.TableName + DataTable");
foreach (DataColumn col in tbl.Columns) Console.WriteLlne("\t" + col.ColumnName + " - " + col.DataType.ToSt ring());
Об объектах DataColumn гораздо больше информации, чем имя. положение и тип данных. Но сейчас мы ненадолго отвлечемся от объекта DataColumn и посмот рим, какие же данные DataAdapter поместил в новый объект DataTable.
Просмотр данных, возвращаемых объектом DataAdapter Объект DataTable сильно отличается от соответствующих объектов предыдущих моделей доступа к данным. Объекты Recordset моделей ADO и DAO, объект rdoRe sultset модели RDO и объект DataReader модели ADO.NET поддерживают концеп цию текущего ряда данных. Все эти объекты позволяют просматривать резуль таты запроса по одной записи за раз. Объекты Recordset и rdoResultset позволяют управлять текущей записью при помощи методов типа MoveFirst, MovePrevious, MoveNexi и MoveLast, В объекте DataTable ADO.NET реализован иной подход, более соответствую щий XML-документам;
он позволяет в любой момент времени обратиться к лю бому узлу дерева. При использовании объекта DataTable всегда доступны все за писи Ч 24 часа в сутки. 7 дней в неделю, 365 дней в году и... ну, в общем, вы поня ли основную идею.
180 Часть III Автономная работа с данными: объект DataSei модели ADO.NET Класс DataTable предоставляет свойство Rows, возвращающее набор объектов DataRoiv, доступных в объекте DataTable. Теперь я расскажу, как с'помощью объектов DataRow просматривать результаты запроса.
Объект DataRow Объект DataRow позволяет просматривать и изменять содержимое отдельной за писи в объекте DataTable. Чтобы назначить объект DataRoiv конкретной записи в объекте DataTable, воспользуйтесь свойством Rows этого объекта. Это свойство возвращает объект DataRowCollection, содержащий набор объектов DataRow. Как и большинство объектов-наборов, DataRowCollection позволяет указать порядко вый номер элемента, к которому вы хотите обратиться.
Следующий фрагмент кода с помощью метода Fill объекта DataAdapter выби рает результаты запроса в новый объект DataTable, Затем код назначает первую возвращенную запись объекту DataRoiv и выводит содержимое двух полей этой записи.
Visual Basic.NET Dim strConn, strSQL As String strConn = "Provider=SQLOLEDB;
Data Source=(local)\NetSDK;
" & "Initial Catalog=Northwind;
Trusted_Connection=Yes;
" strSQL = "SELECT QrderlD, Customer-ID, EmployeelD, OrderDate " & _ "FROM Orders" Dim da As New OleDbDataAdapterfstrSQL, strConn) Dim ds As New DataSetO da.FUKds, "Orders") Dim tbl As DataTable = ds.Tables(O) Dim row As DataRow = tbl.Rows(O) Console.WriteLine("OrderID = " & row("Order!D")) Console.WriteLine("CustomerID = " & row("CustomerID")) Visual C#.NET string strConn, strSQL;
StrConn = "Provider=SOLOLEDB;
Data Source=(local)\\NetSDK;
" + "Initial Catalog=Northwind;
Trusted_Connection=Yes;
";
strSQL = "SELECT OrderlD, CustomerlD, EmployeelD, OrderDate " + "FROM Orders";
OleDbDataAdapter da = new OleDbDataAdapterCstrSQL, strConn);
DataSet ds = new DataSetO;
da.Fillfds, "Orders");
DataTable tbl = ds.Tables[0];
DataRow row = tbl.Rows[0];
Console.WriteLine("OrderlD = " + row["OrderID"]);
Console.WrtteLineC'CustomerlD = " + row["CustomerID"]);
Как видно, назначив объект DataRow определенной записи объекта DataTable, вы можете работать с данными конкретного поля так же, как и с данными объек ГЛАВА 6 Работа с объектами DataSet та DataReader. У объекта DataRow есть шраметризированное свойство Пет, воз вращающее содержимое конкретного поля. Можно указывать имя поля, как в пре дыдущем фрагменте кода, или целое число, соответствующее порядковому номе ру столбца в объекте DataTable. Как и в случае с объектом DataReader, при поиске по индексу данные возвращаются быстрее, чем при построчном поиске. Я исполь зовал имена столбцов только для того, чтобы сделать код более понятным.
Просмотр содержимого объекта DataRow Что, если вы хотите написать универсальную процедуру для вывода содержимо го DataRow't Например, процедуру, которая принимает объект DataRow и выво дит имена и значения его полей?
Если для этого использовать объект DataReader, следует проверить его значе ние свойства FieldCount и определить число поля, Затем с помощью свойств Get Name и Item получить имя и содержимое каждого поля. Однако у объекта DataRow нет аналога свойства FieldCount объекта DataReader.
Вместо него объект DataRow предоставляет свойство Table, возвращающее объект DataTable, который содержит объект DataRow. Это свойство позволяет вер нуться к DataTable и получить общее число и имена столбцов. Следующий фраг мент выводит содержимое и имена полей объекта DataRow при помощи свойства Table данного объекта:
Visual Basic.NET Private Sub DisplayRowfByVal row As Dataflow) Dim tbl As DataTable = row.Table Dim col As DataColumn For Each col In tbl.Columns Console.WriteLine(vbTab & col.ColumnName & ": " & row(col)) Next col End Sub Visual C#.NET static void DisplayRow(Dataflow row) { DataTable tbl = row.Table;
foreach (DataColumn col in tbl.Columns) Console.WriteLine("\t" + col.ColumnName + ": " + row[col]);
} Здесь показан третий способ просмотра содержимого конкретного поля. Ме тод Item объекта DataRow принимает объект DataColumn. На момент написания этой книги выборка содержимого записи посредством передачи объекта Data Column обеспечивала чуть более высокую производительность (примерно на 6% выше), чем поиск по порядковым номерам.
Просмотр объектов DataRow в объекте DataTable Перемещаться по объектам DataRow в объекте DataTable так же просто, как пере мещаться по любому другому набору в.NET Framework. Используйте цикл For или Автономная работа с данными: объект DataSet модели ADO. NET 1 82 Часть For Each языка по вашему выбору. Следующий фрагмент кода с помощью приво дившейся ранее процедуры DisplayRoiv просматривает содержимое объекта Data Table, созданного при вызове метода DataAdapterfitt.
Visual Basic.NET Dim strConn, strSQL As String strConn = "Provider=SQLOLEDB;
Data Source=(local)\NetSDK;
" & "Initial Catalog=Northwind;
Trusted_Connection=Yes;
" strSQL = "SELECT OrderlD, CustomerlD. EmployeelD, OrderDate " & _ "FROM Orders" Dim da As New 01eDbDataAdapter(strSQL, strConn) Dim ds As New DataSetf) da.FilKds, "Orders") Dim tbl As DataTable = ds.Tables(O) Dim row As DataRow Dim intCounter As Integer For Each row In tbl. Rows intCounter += Console. WriteLine{"Contents of row #" & intCounter) DisplayRow(row) Next row Visual C#.NET string strConn, strSQL;
strConn = "Provider=SQLOLEDB;
Data Sou rce=( local )\\NetSDK;
" + "Initial Catalog=Northwind;
Trusted_Connection=Yes;
";
strSQL = "SELECT OrderlD, CustomerlD, EmployeelD, OrderDate " + "FHOM Orders";
OleDbDataAdapter da = new 01eDbDataAdapter(strSQL, strConn);
DataSet ds = new DataSetO;
da.FilKds, "Orders");
DataTable tbl = ds.Tables[0];
int intCounter;
foreach (DataRow row in tbl. Rows) intCounter++;
Console. WriteLinef'Contents of row ft" + intCounter);
DisplayRow(row);
Проверка данных в объекте DataSet БД предоставляют различные механизмы проверки своих данных. В БД North wind определено множество правил и ограничений. Значения поля CustomerlD табли цы Customers не должны превышать 5 символов и должны быть уникальными в пределах таблицы. Таблица Orders генерирует для каждой записи новое значение ГЛАВА 6 Работа с объектами DataSet OrderlD и требует, чтобы значение CustomerlD каждой записи соответствовало одной из записей таблицы Customers.
Иногда с помощью этих же правил необходимо проверить данные в прило жении перед отправкой их в БД. Например, в Интернет-магазине вы открыли стра ницу для оформления покупки положенных в корзину товаров. Большинство ма газинов до отправки сведений о заказе в соответствующую БД убедятся, что вы заполнили все обязательные поля.
Подобная логика иногда кажется избыточной, поскольку в БД скорее всего уже определены похожие правила проверки. Тем не менее, добавляя правила провер ки в приложение, вы повышаете его производительность. Если пользователь слу чайно или намеренно не ввел номер кредитной карты, Web-страница с легкос тью, не обращаясь к БД, определит, что успешная отправка сведений о заказе не возможна. Еще одно преимущество такого подхода Ч небольшое снижение сете вого трафика и нагрузки на БД.
Объект DataSet модели ADO.NET предоставляет множество механизмов про верки данных, аналогичных механизмам БД. Эти механизмы, также называемые ограничениями (constraints), можно разделить на две категории Ч ограничения уровня столбца и ограничения уровня таблицы, Свойства объекта DataColumn, используемые для проверки данных Объект DataColumn предоставляет ряд свойств для проверки данных.
Свойство Readonly Простейший способ гарантировать правильность данных Ч запретить пользова телям изменять их. Чтобы сделать данные объекта DataColumn доступными толь ко для чтения, задайте его свойству Readonly значение True, Свойство AllowDBNulI Одни поля БД требуют обязательно указать их значение, а друтие принимают пустые значения Ч NULL. Объект DataColumn предоставляет свойство AllowDBNulI, опре деляющее, принимает ли столбец объекта DataSet значения NULL.
Свойство MaxLength Многие БД ограничивают длину значения поля. Так, поле CustomerlD таблицы Customers принимает строку длиной до 5 символов, а поле CompanyName Ч строку длиной до 40 символов. Определить такое же ограничение на объекте DataColumn позволяет свойство MaxLength.
Свойство Unique Свойство Unique объекта DataColumn позволяет указать уникальность значений столбца. При значении True этого свойства ADO.NET просматривает значения соответствующих полей каждой записи объекта DataTable. Если вы, добавляя или изменяя запись объекта DataTable, создадите в столбце с ограничением на уни кальность идентичное значение, ADO.NET сгенерирует исключение Constraint Exception.
Часть III Автономная работа с данными: объект DataSet модели ADO.NET 1 Набор Constraints объекта DataTable Для проверки данных в объекте DataSet предназначены также свойства объекта DataTable. В объектной модели ADO.NET педусмотрено два класса, позволяющих определять ограничения в объекте DataTable, Ч UniqueConstraint и ForeignKey Constraint;
они происходят от класса Constraint. Объект DataTable предоставляет свойство Constraints, при помощи которого удается добавлять, изменять и просмат ривать ограничения в объекте DataTable.
Ограничение UniqueConstraint Задав свойству Unique объекта DataColumn значение True, вы определите в объек те DataTable, содержащем соответствующий столбец, ограничение на уникальность.
Одновременно с этим в набор Constraints объекта DataTable добавляется ограни чение UniqueConstraint. Задать значение свойства Unique объекта 'DataColumn проще, чем создать новый объект UniqueConstraint в наборе Constraints объекта DataTable.
Тем не менее иногда требуется создать объект UniqueConstraint явно, например, если необходимо гарантировать уникальность комбинаций значений различных полей.
Ограничение РптагуКеу Первичный ключ Ч особая разновидность ограничения на уникальность. У объекта DataRowCottection ADO.NET есть метод Find, позволяющий искать запись в объекте DataTable по значениям ее полей первичного ключа (подробнее о методе Find Ч в главе 8).
row = MyTable.Rows.FindC"ALFKI") В объекте DataTable предусмотрено множество ограничений на уникальность, однако первичный ключ может быть только один. Задать и просмотреть первич ный ключ объекта DataTable позволяет свойство РптагуКеу Ограничение ForeignKeyConstraint В объект DataTable также разрешается'добавлять ограничения на внешний ключ.
Пример такого ограничения я приводил буквально пару страниц назад. Значение поля CustomerlD каждой записи таблицы Orders БД North wind должно соответ ствовать одной из записей таблицы Customers. Чтобы определить подобное огра ничение на данных объекта DataSet, создайте объект ForeignKeyConstraint и добавьте его в набор Constraints объекта DataTable, данные которого требуется проверять, Обычно создавать объект ForeignKeyConstraint явно не требуется. При созда нии объекта DataRelation, связывающего два объекта DataTable вашего объекта DataSet, автоматически создается объект ForeignKeyConstraint. В следующей главе я познакомлю вас с объектом DataRelation и его использованием для работы с реляционными данными.
Примечание ADO.NET ничего не знает о данных БД. Ограничения, определя емые вами на столбцах и таблицах объекта DataSet, действительны только в пределах этого объекта. Очень важно помнить об этом и вот почему.
Предположим, вы определили на объекте DataTable ограничение Uni queConstraint, основанное на столбце CustomerlD. Если вы добавите за ГЛАВА 6 Работа с объектами DataSet пись, значение поля CustomerlD которой Ч ZZZ2Z, ADO.NET сгенериру ет исключение только в случае, если в объекте Data-Table есть другая за пись с таким же значением этого поля, Соблюдение ограничений на внешний ключ осуществляется таким же образом. Если определить на объекте DataTable Orders внешний ключ, основанный на столбцах CustomerlD объектов DataTable Orders и Custo mers, ADO.NET позволит вам добавлять только те заказы, значение поля CustomerlD которых будет соответствовать одной из записей объекта DataTable Customers. Если вы попытаетесь добавить заказ, значение поля CustomerlD которого есть в БД, но отсутствует в объекте DataTable Custo mers, ADO.NET сгенерирует исключение.
Получение информации схемы с помощью метода DataAdapter.FiliSchema На проверку данных требуется время. Во многих случаях вы не станете назначать свойства проверки данных объекта DataSet, чтобы DataAdapter, не имея на то яв ных указаний, не задавал соответствующие свойства объектов DataColumn и не добавлял ограничения в набор Constraints объекта DataTable, создавая объект DataTable посредством вызова метода DataAdapterftil.
Есть два способа сообщить объекту DataAdapter, что при добавлении столбцов в объект DataTable следует получить о них информацию схемы (сведения об ог раничениях): задать свойству MissingSchemaAction объекта DataAdapter значение АсШ WithKey или вызвать метод DataAdapterfillSchema (подробнее об этом Ч в главе 5).
Пробуйте это дома, и только дома...
Некоторые функции ADO.NET следует использовать в приложениях как можно реже. Одна из них Ч выборка информации схемы для объекта DataSet с помощью объекта DataAdapter.
Получение информации схемы средствами Dc&aAdapter экономит время в период разработки. Фактически Visual Studio.NET в период разработки генерирует объекты DataSet с помощью DataAdapter (подробнее об этом далее в этой главе). Если вы создаете небольшое приложение для подтвер ждения концепции, то согласитесь, что при использовании DataAdapter для получения информации схемы требуется писать меньше кода.
Однако если ваша программа не предназначена для выполнения произ вольных запросов, вам должны быть известны возвращаемые столбцы, и поэтому функции типа DataAdapterJFiUSchema в готовых приложениях вам не потребуются.
Если вы укажете объекту DataAdapter выбрать дополнительную информацию схемы с помощью метода FittSchema, для каждого нового объекта DataColumn объект DataAdapter получит из БД гораздо больше сведений, чем просто имя и тип дан ных. Просмотрев любой из этих объектов DataColumn, вы убедитесь, что их свой ствам Readonly, AllowDBNulL MaxLengtb и Unique заданы правильные значения Кроме того, DataAdapter попытается сгенерировать для объекта DataTable пер вичный ключ. Именно на данном этапе при выборке информации схемы проис ходит значительное снижение производительности, и вот почему.
Автономная работа с данными: объект DaiaSet модели ADO.NET 186 Часть III DataAdapter должен обратиться к БД и определить, на какую таблицу ссылает ся ваш запрос, а затем снова обратиться к БД и получить информацию о первич ном ключе этой таблицы. Если на таблице первичный ключ не определен, Data Adapter запросит информацию об индексах таблицы. Получив эти сведения, Data Adapter просмотрит столбцы, возвращенные запросом. Таким образом гаранти руется, что, если таблица содержит первичный ключ из двух столбцов и запрос не ссылается сразу на оба этих поля, DataAdapter не станет создавать первичный ключ для объекта DataTable.
Примечание Объект DataAdapter также задает свойство Autolncrement новых объектов DataColumn. Чуть позже в этой главе я вкратце расскажу о дан ном свойстве, Подробнее о его использовании Ч в главе 11.
Создание объектов DataTable в коде Вы уже умеете создавать объекты DataTable с помощью методов Fill и FillSchema объекта DataAdapter и знаете, что необходимо создавать собственные объекты DataTable, особенно когда требуется проверять данные средствами ограничений уровня столбца или таблицы. Теперь речь пойдет о том, как создавать объекты DataTable программно.
Создание объекта DataTable Объект DataTable создается так же, как и объект DataSet. У объекта DataTable есть дополнительный конструктор, позволяющий задать значение свойства TableName этого объекта:
Visual Basic.NET Dim tbl As New DataTable("TableName") Console,WriteLine(tbl.TableName) Visual C#.NET DataTable tbl = new DataTableC"TableName");
Console.WriteLIne(tbl.TableName);
Добавление объекта DataTable в набор Tables объекта DataSet Созданный объект DataTable можно средствами метода DataTableCollectionAdd добавить в набор Tables имеющегося объекта DataSet:
Visual Basic.NET Dim ds As New DataSet{) Dim tbl As New DataTable("Customers") ds.Tables.Add(tbl) Visual C#.NET DataSet ds = new DataSetQ;
DataTable tbl = new DataTableC'Customers");
ds.Tables.Add(tbl);
ГЛАВА 6 Работа с объектами DataSet Кода, в общем-то немного, но вечно умные разработчики Microsoft предоста вили нам еще более простой способ добавления нового объекта DatdTable в на бор Tables объекта DataSet, перегрузив метод Add объекта DataTablesCollection.
Создать новый объект DataTable и добавить его в набор Tables объекта DataSet можно одним вызовом:
Visual Basic.NET Dim ds As New DataSet() Dim tbl As DataTable = ds.Tables.Add("Customers") Visual C#.NET DataSet ds = new DataSetC);
DataTable tbl = ds.Tables.Actd("Customers");
Чтобы определить, находится ли объект DataTable в объекте DataSet, достаточно проверить значение свойства DataSet объекта DataTable. Если объект DataTable находится в наборе Tables объекта DataSet, свойство DataSet вернет DataSet. В противном случае оно вернет Nothing или null, в зависимости от используемого вами языка. Свойство DataSet объекта DataTable доступно только для чтения.
Отмечу также, что объект DataTable может находиться только в одном объек те DataSet. Чтобы добавить DataTable в несколько объектов DataSet, воспользуй тесь методом Сору или Clone. Метод Сору создает новый объект DataTable с такой же структурой и тем же набором записей, что и у оригинального объекта DataTable.
Метод Clone создает объект DataTable с такой же структурой, что и метод Сору, но без записей.
Добавление столбцов в объект DataTable Пора дополнить скелет* нашего нового объекта DataTable небольшим количеством мяса. Для хранения результатов запроса объекту DataTable нужны столбцы. В од ном из предыдущих разделов рассказывалось, что объект DataAdapter способен создавать объекты DataColumn. Теперь пришло время создать новые объекты DataColumn. Добавить эти объекты в набор Columns объекта Table позволяет код, который практически идентичен коду для добавления объекта DataTable в набор Tables объекта DataSet, Visual Basic.NET Dim ds As New DataSetO Dim tbl As DataTable = ds.Tables.Add("Customers") Dim col As DataColumn = tbl.Columns.Add("CustomerID") Visual C#.NET DataSet ds = new DataSetO;
DataTable tbl = ds.Tables.Add{"Customers"};
DataColumn col = tbl.Columns.AddC'CustomerlD");
Часть III Автономная работа с данными: объект DalaSet модели ADO.NET 1 Указание типа данных объекта DataColumn При создании нового объекта DataColumn следует также указать тип хранящихся в нем данных. Просмотреть или задать тип данных объекта DataColumn можно посредством свойства DataType этого объекта. Изменять значение свойства Datatype объекта DataColumn допустимо, пока вы не добавили данные в набор Rows объекта DataTable;
затем это значение разрешается только просматривать.
И хотя выбираемый вами тип данных объекта DataColumn зависит от типа данных соответствующего столбца БД, между типами данных БД и объектов Data Column нет прямого сопоставления, Например, Microsoft SQL Server предоставляет ряд типов для строковых дан ных. Определяя структуру таблицы БД SQL Server, можно указать, что строковые данные будут храниться как строка фиксированной или переменной длины, а также что данные должны храниться в однобайтовом (ANSI) или двубайтовом (Unicode) представлении.
Однако в ADO.NET строка Ч это строка. Независимо от того, имеет ли тип данных БД переменную или фиксированную длину, использует ли он однобайто вое или двубайтовое представление, тип данных объекта DataColumn Ч просто string. Свойство DataType объекта DataColumn работает с типами данных.NET, a не типами данных БД.
Значение свойства DataType объекта DataColumn по умолчанию Ч string. У объекта DataColumn есть конструктор, позволяющий указать имя и тип данных создаваемого столбца. Метод Add объекта DataColum^Collection аналогичным об разом перегружен и позволяет указать значения свойств ColumnName и DataType нового объекта DataColumn, создаваемого в объекте DataTable:
Visual Basic.NET Dim ds As New DataSetO Dim tbl As DataTable = ds.Tables.Add("0rders") Dim col As DataColumn = tbl.Columns.AddC'OrderlD", GetType(Integer)) Visual C#.NET DataSet ds = new DataSetO;
DataTable tbl = ds.Tables.AdcK"Orders");
DataColumn col = tbl.Columns.AddC'OrderlD", typeof(int));
Тип данных свойства DataType Ч Type. Предыдущий фрагмент кода показыва ет, как получить значение Туре, соответствующее типу данных integer. В целях обратной совместимости Visual Basic.NET и Visual С*.NET генерируют типы по средством разных функций. До появления.NET функция (урео/былэ. и в C++, и в Visual Basic, однако возвращаемые ей сведения зависели от используемого языка программирования. В результате в Visual Basic добавили функцию GetType возвра щающую информацию о типах.
Добавление первичного ключа Ранее я уже объяснял, чем программная проверка данных в объектах DataColumn и DataTable лучше использования объекта DataAdapter, обращающегося для про верки данных к БД. Мои пояснения оказались бы пустым звоном, если бы я не ГЛАВА 6 Работа с объектами DataSet рассказал, как задать свойства объектов DataColumn и DataTable, обеспечивающие проверку данных.
Как вы помните, я начал с описания свойств AllowDBNull Readonly, MaxLength и Unique объекта DataColumn и того, как с их помощью проверять данные стол бцов. Задать значения этих свойств в коде очень просто.
Visual Basic.NET Dim ds As New DataSet() Dim tbl As DataTable = ds.Tables.Add{"Customers") Dim col As DataColumn = tbl.Columns.AddC'CustomerlD") col.AllowDBNull = False col.MaxLength = col.Unique = True Visual C#.NET DataSet ds = new DataSet();
DataTable tbl = ds.Tables.Add("Customers");
DataColumn col = tbl.Columns.AddC'CustomerlD");
col.AllowDBNull = false;
col.MaxLength = 5;
col.Unique = true;
Определить первичный ключ для объекта DataTable гораздо сложнее. Свойство PrimaryKey содержит массив объектов DataColumn, в связи с чем ему нельзя про сто задать имена столбцов, составляющих первичный ключ.
Первичный ключ одних объектов DataTable состоит из единственного столб ца, а других Ч из комбинации столбцов. В следующем фрагменте кода вам встре тятся оба этих варианта. Первичный ключ таблицы Customers состоит из одного столбца,CustomerlD, а первичный ключ таблицы Order Details Ч из столбцов OrderlD и ProductlD. В каждом случае нужно создать массив объектов DataColumn и задать его свойству РггтагуКеу объекта DataTable:
Visual Basic.NET Dim ds As New DataSetС) 'Создаем объект DataTable с именем Customers With ds,Tables,Add("Customers").Columns.Add("CustomerlD", GetType(String)).PrimaryKey = New DataColumnO {.Columns("CustomerID")} End With 'Создаем объект DataTable с именем Order Details With ds.Tables.Add("0rder Details").Columns.Add("OrderlD", GetType(Integer)).Columns.Add("ProductlD", GetType(Integer)).PrimaryKey = New DataColumnO {.Columns("OrderID"), _.Columns("ProductlD")} End With Автономная работа с данными: объект DataSet модели ADO.NET 1 90 Часть III Visual C#.NET DataSet ds = new DataSetO;
DataTable tbl;
//Создаем объект DataTable с именем Customers tbl = ds.Tables.Add("Customers");
tbl. Columns. Add( "Customer-ID", typeof( string));
tbl.PrimaryKey = new DataColumn[] {tbl.Columns["CustomerID"]};
//Создаем объект DataTable с именем Order Details tbl = ds.Tables.Add("0rder Details");
tbl.Columns.Add("OrderID", typeof(int));
tbl.Columns.Add("ProductID", typeof(int));
tbl.PrimaryKey = new DataColumn[] {tbl.Columns["Order!D"], tbl.Columns["ProductID"]};
Примечание После того как вы определите первичный ключ объекта DataTable, ADO.NET автоматически задаст свойству Мои ХDBNull объектов DataColumn, составляющих этот ключ, значение False, Добавление других ограничений Наиболее широко используемое ограничение Ч это первичный ключ, однако в объект DataTable разрешается добавлять также и уникальные и внешние ключи. У набора Constraints объекта DataTable есть перегруженный метод Add, позволяю щий определять ограничения Primary Key, UniqueKey и ForeignKey. Следующий фрагмент кода с помощью метода Add добавляет простой и составной первичные ключи, а также создает внешний ключ.
Код добавляет в объект DataTable Customers уникальный ключ, основанный на столбце CustomerlD, а объект DataTable Order Details Ч уникальный ключ, осно ванный на столбцах OrderlD и ProduclID. Кроме того, он создает в таблице Order Details внешний ключ, чтобы гарантировать, что значениям столбца OrderlD со ответствует одна из записей таблицы Orders.
Для каждого случая в коде реализовано два способа создания ограничения.
Первый пример явно создает новый объект Constraint и добавляет его в набор Constraints объекта DataTable. Выглядит это так:
tbl.Constralnts.AddfNew UniqueConstraint(...)) Метод Add объекта ConstraintCollection принимает любой объект, наследующий от объекта Constraint, и поэтому ему можно просто передать объект UniqueConstraint или ForeignKey Constraint.
Второй пример создает ограничение и добавляет его в набор, tbl. Constraints.Add("ConstraintName", Columnlnformation) Метод Add перегружен и позволяет создавать ограничения PmnaryKey, UniqueKey и ForeignKey. Тем не менее обычно я избегаю использовать его и предпочитаю Работа с объектами DataSet ГЛАВА создавать ограничения явно, поскольку- это повышает удобочитаемость кода. Ниже я привожу оба синтаксиса, так как в некоторых случаях вам потребуются пере груженные методы Add, создающие новое ограничение и сразу добавляющие его в набор.
Visual Basic.NET Dim ds As New DataSetQ 'Создаем новый объект DataTable с именем Customers With ds.Tables.Add("Customers").Columns.Add("CustomsrID", GetType(String 'Добавляем уникальный ключ, основанный на столбце CustomerlD,Constraints.Add(New UniqueConstraint(.Columns("CustomerlD")) 'или.Constraints.Add("UK_Customers",.Columns("CustomerlD"), False) End With 'Создаем новый объект DataTable с именем Order Details With ds.Tables.Add("Order Details").Columns.Add("OrderID", GetTypeCInteger)).Columns.Add("ProductID", GetType{Integer 'Добавляем уникальный ключ, основанный на столбцах OrderlD и ProductID Dim cols As New DataColumn() {.Columns("OrderID"), _.Columns{"ProductID")}.Constraints.Add(New UniqueConstraint(cols)) 'или.Constraints.Add("UK_Order Details", cols, False) 'Добавляем ограничение ForeignKeyConstraint, основанное на столбце OrderlD 'и требующее наличия соответствующих значений OrderlD в таблице Orders.Constraints.Add(New ForeignKeyConstraint (ds.Tables("Orders").Columns("OrderlD"), _.Columns("OrderIO") 'or.Constraints.Add("FK_Order Details_0rders", ds.Tables("Orders").Columns{"OrderlD"), _,Columns("OrderID" End With Visual C#.NET DataSet ds = new DataSetC);
DataTable tbl;
//Создаем новый объект DataTable с именек Customers tbl = ds. Tables.Add{"Customers"};
tbl,Columns.Add("CustomerlD", typeof(string));
//Добавляем уникальный ключ, основанный на столбце CustomerlD tbl.Constraints.Add(new UniqueConstraint(tbl.Columns["CustomerlD"]));
//или 192 Часть III Автономная работа с данными: объект DataSet модели ADO.NET tbl.Constraints.Add("UK_Customers", tbl.Columns["CustomerlD"], false);
//Создаем новый объект DataTable с именем Order Details tbl = ds.Tables.Add("0rder Details");
tbl.Columns.Add("OrderID", typeof(int));
Х tbl.Columns.Add("ProductID", typeof(int));
//Добавляем уникальный ключ, основанный на столбцах OrderlD и ProductID DataColumn[] cols = new DataColumn[] {tbl.Columns["OrderID"], tbl.Columnst"ProductID"]};
tbl.Constraints.Add{new UniqueConstraint(cols));
//или tbl.Constraints.Add("UK_Order Details", cols, false);
//Добавляем ограничение ForeignKeyConstraint, основанное на столбце OrderlD //и требующее наличия соответствующих значений OrderlD в таблице Orders tbl.Constraints.Add(new ForeignKeyConstraint (ds.Tables["Orders"].Columns["OrderlD"], tbl.Columns["OrderlD"]});
//или tbl.Constraints.Add("FK_Order Details_0rders", ds.Tables["Orders"].Columns["OrderlD"], tbl.Columns["OrderID"]);
Использование столбцов с автоинкрементом ADO.NET поддерживает столбцы с автоматическим увеличением значения (авто инкрементом) при помощи трех свойств объекта DataColumn Ч Autolncrement, AutoIncrementSeed и AutolncrementStep, Если необходимо, чтобы ADO.NET генерировала для новых записей значения автоинкремента, задайте свойств Autolncrement объекта DataColumn значение True-.
Visual Basic.NET Dim ds As New DataSetC) Dim tbl As DataTable = ds.Tables.Add("0rders") Dim col As DataColumn = tbl.Columns.Add("OrderID", GetType(Integer)) col.Autolncrement = True col.AutoIncrementSeed = - col.AutoIncrementStep = - col.Readonly = True Visual C#.NET DataSet ds = new DataSetf);
DataTable tbl = ds.Tables.Add("0rders");
DataColumn col = tbl.Columns.Add("OrderID", typeof(int));
col.Autolncrement = true;
col.AutoIncrementSeed = -1;
ool.AutoIncrementStep = -1;
col.Readonly = true;
Работа с объектами DataSet ГЛАВА Данный фрагмент кода пометил OrderlD как столбец с автоинкрементом и задал свойствам AutoIncrementSeed и AutoIncrementStep значение -1. Настоятельно реко мендую вам задать этим свойствам указанное значение, и вот почему.
Свойства AutoIncrementSeed и AutoIncrementStep определяют, как ADO.NET ге нерирует новые значения. Например, если вы работаете с пустой таблицей, ADO.NET задаст полю с автоинкрементом первой записи значение свойства AutoIncrement Seed. Генерировать последующие значения автоинкремента ADO.NET будет на основе значения свойства AutoIncrementStep.
Так, если значение свойств Autolncrernent Ч True, а свойств AutoIncrementSeed и AutoIncrementStep Ч 2, ADO.NET сгенерирует для первых пяти записей следую щие значения автоинкремента: 2, 4, 6, 8, 10.
Когда вы добавляете записи в объект DataTable с помощью метода DataAdap terfitt, данная модель поведения несколько меняется. Предположим, вы работае те с объектом DataTable, структура которого соответствует таблице Orders БД Northwind, и задали свойствам AutoIncrementSeed и AutoIncrementStep объекта DataColumn OrderlD значение 5. Если вы добавите записи в этот объект DataTable, пока он пуст, значениея поля OrderlD данных записей равны 5, 19, 15, 20 и т. д, Однако, если вы сначала добавили записи в объект DataTable, вызвав метод Data Adapterfill, и затем добавляете новые записи средствами метода DataTableRowsAdd, значение поля OrderlD новых записей будет зависеть от полученных из БД дан ных. ADO.NET сгенерирует новые значения автоинкремента, основываясь на мак симальном значении автоинкремента, имеющемся в объекте DataTable, и значе нии свойства AutoIncrementStep.
Предположим, в этом примере максимальное текущее значение поля OrderlD в объекте DataTable Ч 973. Если сейчас добавить новую запись, ADO.NET сложит значение свойства AutoIncrementStep (5) и максимальное текущее значение поля OrderlD и получит новое значение автоинкремента Ч 978.
Важно помнить, что ADO.NET знает только о данных, хранящихся в объекте DataTable. Следующее значение автоинкремента, которое сгенерирует БД, объек тной модели ADO.NET неизвестно. Как я сказал, максимальное значение поля OrderlD в объекте DataTable, заполненном результатами нашего запроса Ч 973.
Вероятно, запрос выбрал только заказы конкретного клиента:
SELECT OrderlD, CustomerlD, OrderDate FROM Orders WHERE CustomerlD = 'ALFKI' В БД значение поля OrderlD может быть больше, чем в объекте DataTable.
ADO.NET не способна узнать об этом невероятно, сгенерирует для новых запи сей объекта DataTable значения автоинкремента, уже используемые в БД.
Я упомянул об этом, чтобы помочь вам понять, как работают функции авто инкремента в ADO.NET. Зная это, вы примете грамотные решения относительно создания новых значений автоинкремента средствами ADO.NET, В период разработки ADO.NET и.NET Framework один разработчик спросил меня, есть ли у объекта DataTable средства постраничного представления инфор мации Ч ему требовалось возвращать или отображать лишь часть записей DataTable.
Вместо того чтобы считать записи и создавать сложные фильтры, основанные критериях поиска и упорядочении записей, постраничную разбивку удастся орга низовать гораздо проще, позволив ADO.NET использовать средства автоинкремента и считать с их помощью добавляемые в объект DataTable записи.
1 94 Часть III Автономная работа с данными: объект DafaSet модели ADO.NET Рекомендации по использованию автоинкремента Правильно: использовать средства автоинкремента ADO.NET.
Неправильно: передавать в БД значения автоинкремента, генерируемые ADO.NET, Эти значения Ч не более, чем метки. Пусть реальные значения генерирует БД Б главе 11 рассказывается, как передать генерацию значе ний базе данных и как выбирать эти значения в соответствующие записи объекта DataTable.
Неправильно: выводить значения автоинкремента новых записей, не переданных в БД. БД скорее всего сгенерирует значения, отличные от со зданных ADO.NET. Пользователь вашего приложения может не знать, что значение автоинкремента, генерируемое ADO.NET, Ч это метка. Если ваше приложение применяется для ввода информации о заказах, нужно ли вам, чтобы клерк, принимающий заказы от клиентов, подумал, что значение поля Order ID, сгенерированное ADO.NET, Ч точное значение, и сообщил это значение клиенту?
Правильно: задавать свойствам AutoIncrementSeed и AutolncrementStep зна чения -1. Это гарантирует, что ADO.NET будет генерировать значения-мет ки, которых нет в БД. Даже если вы выводите эти значения в приложении, у пользователя не будет шанса ошибочно предположить, что ADO.NET ге нерирует значения автоинкремента, соответствующие значениям, генери руемым БД, Следующий фрагмент кода заполняет объект DataTable результатами просто го запроса. Перед заполнением таблицы код добавляет в нее столбец с автоинк рементом. Поскольку запрос не возвращает значений для этого столбца, ADO.NET генерирует для всех возвращенных запросом записей собственные значения дан ного столбца.
В коде используются объекты DataView и DataRowView. Я подробно расскажу о них в главе 8, однако в данном фрагменте их использование вполне наглядно.
Заполнив объект DataTable результатами запроса, я с помощью объекта DataView вывожу лодну страницу объекта DataTable на экран.
Да, я создал структуру объекта DataTable с помощью метода FittSchema. но по ступил так. только чтобы сократить фрагмент кода. В реальном приложении я не стал бы использовать этот метод. Клянусь честью скаута.
Visual Basic.NET Dim ds As New DataSetO Dim strConn, strSQL As String strConn = "Provider=SQLOLEDB;
Data Source=(local)\NetSDK;
" i _ "Initial Catalog=Northwind;
Trusted_Connectton=Yes;
" strSQL = "SELECT CustomerlD, CompanyName, ContactName " & _ "FROM Customers" Dim da As New 01eDbDataAdapter(strSQL, strConn) da.FillSchemafds, SchemaType.Source, "Customers") Dim tbl As DataTable = ds.Tables("Customers") Dim col As DataColumn = tbl.Columns.Add("RowID", GetType(lnteger)) ГЛАВА 6 Работа с объектами DataSet col.AutoIncrement = True col.AutolncrementSeed = col.AutoIncrementStep = da.Fill(ds, "Customers") Dim vue As New DataView(tbl) Dim intPageSize As Integer = Dim intPageNum As Integer = vue.RowFilter = "RowID > " & (intPageNum - 1) * intPageSize & " AND RowID <= " & intPageNum * intPageSize Dim row As DataRowView Dim intCounter As Integer For intCounter = 0 to vue.Count - row = vue(intCounter) Console.WriteLine(row("RowID") & vDTab & row("CustomerID") & vbTab & row("CompanyName")) Next intCounter Visual C#.NET DataSet ds = new DataSetO;
string strConn, strSQL;
strConn = "Provider=SQLOLEDB;
Data Source=(local)\\NetSDK;
" + "Initial Catalog=Northwind;
Trusted_Connection=Yes;
";
strSQL = "SELECT CustomerlD, CompanyName, ContactName " + "FROM Customers";
OleDbDataAdapter da = new OleDbDataAdapterfstrSQL, strConn);
da.FillSchema(ds, SchemaType.Source, "Customers");
DataTable tbl = ds.Tables["Customers"];
DataColumn col = tbl.Columns.Add("RowID", typeof(int));
col.AutoIncrement = true;
col.AutolncrementSeed = 1;
col.AutoIncrementStep = 1;
da.Fill(ds, "Customers");
DataView vue = new DataView(tbl);
int intPageSize = 10;
int intPageNum = 3;
vue.RowFilter = "RowID > " + (intPageNum - 1) * intPageSize + " AND RowID <= " + intPageNum * intPageSize;
DataRowView row;
for (int intCounter = 0;
intCounter < vue.Count;
intCounter++) { row = vue[intCounter];
Console.WriteLine(row["RowID"] + "\t" + row["CustomerID"] + "\t" + row["CompanyName"]);
} Стоит ли применять этот код как средство постраничного вывода данных,:,ля Web-приложений? Думаю, нет. Он плохо масштабируется. Подробнее о способах постраничного вывода данных Ч в главе 14. Данный фрагмент лишь показывает, как решить некоторые интересные проблемы при помощи средств автоинкремента ADO.NET.
1 96 Часть III Автономная работа с данными: объект DataSet модели ADO.NET Добавление столбца, основанного на выражении Обычно администраторы БД стараются не включать в БД данные, которые мож но получить на основе другой информации БД. Например, в таблице Order Details БД North wind есть столбцы с ценой и количеством каждого входящего в заказ товара, но нет столбца с общей стоимостью этого товара.
Если пользователь видит общую стоимость заказанного товара, ему все равно, где она хранится.
Большинство БД разрешают применять в запросах выражения, благодаря чему в результаты запроса удается включать вычисляемые столбцы. Чтобы БД сосчита ла и вернула общую стоимость отдельных заказанных товаров, воспользуйтесь следующим запросом:
SELECT OrderlD, ProductID, UnitPrice, Quantity, UnitPrice * Quantity AS ItemTotal FROM [Order Details] Заполнив объект DataTable результатами этого запроса, вы получите столбец с результатами нужного выражения. Однако, если откорректировать в объекте DataTable значение поля UnitPrice или Quantity, содержимое вычисляемого поля останется прежним. Это вызвано тем,'что определение вычисляемого поля нахо дится непосредственно в запросе. Когда вы получите результаты запроса, содер жимое вычисляемого столбца не изменится.
ADO.NET позволяет создавать объекты DataColumn, основанные на выражении, Вместо того чтобы включать в запрос выражение, как показано выше, задайте это выражение свойству Expression объекта DataColumn. Когда вы станете просматри вать содержимое вычисляемого поля, ADO.NET на основе выражения вернет со ответствующие значения. После этого можно изменять состав заказа, увеличив или уменьшив количество определенного товара, и эти изменения будут отражаться на значении вычислимого поля.
Следующий фрагмент кода добавляет в объект DataTable Order Detail столбец с общей стоимостью отдельных товаров из состава заказа:
Visual Basic.NET Dim ds As New DataSet<) Dim tbl As DataTable = ds.Tables.Add("0rder Details") tbl.Columns.Add("Quantity", GetType(Integer)) tbl.Columns.Add("UnitPrice", GetType(Decimal)) tbl.Columns.Add("ItemTotal", GetTypefDecimal), "Quantity * UnitPrice") Visual C#.NET DataSet ds = new DataSetC):
DataTable tbl = ds.Tables.Add("0rder Details");
tbl.Columns.Add("Quantity", typeof(int));
tbl.Columns.Add("UnitPrice", typeof(Decimal));
tbl.Columns.Add("ItemTotal", typeof(Decimal), "Quantity - UnitPrice");
ГЛАВА 6 Работа с объектами DataSet Примечание На самом деле это не совсем правильный для БД Northwind спо соб подсчета общей стоимости отдельных товаров из состава заказа. В таблице Order Details есть поле Discount, определяющее действующую скидку на товар. Допустимые значения этого поля Ч от 0 до 1. Если зна чение поля Discount Ч 0,25, на товар действует 25 процентная скидка.
Таким образом, правильный способ подсчета общей стоимости отдель ных товаров из состава заказа:
Quantity * UnitPrice - (1 - Discount) Я намеренно упростил предыдущий фрагмент кода, поскольку хотел заострить ваше внимание на создании вычисляемых полей, не отвлекая на тонкости структуры БД Northwind.
Свойство Expression поддерживает разные функции, включая агрегатные, ссы лающиеся на данные других объектов DataTable из состава DataSet. В следующей главе, посвященной работе с реляционными данными, я покажу, как создавать основанные на выражениях столбцы, использующие агрегатные функции. Под робнее о поддерживаемых свойством Expression функциях Ч в документации MSDN.
Создание объектов DataTable, соответствующих таблицам Customers, Orders и Order Details Я уже показал многие возможности объектов DataSet, DataTable и DataColumn.
Теперь давайте объединим их в один объект DataSet, Следующий фрагмент кода создает объект DataSet, содержащий три объекта DataTable (показанные ранее фрагменты кода демонстрировали, как создать эти объекты DataTable по отдель ности). Параллельно код задает свойства объектов DataColumn (включая DataType, AllowDBNull и Autolncrement), а также ограничения UniqueKeyConstraint и Foreign KeyConstraint, Объект DataSet, создаваемый этим фрагментом кода, почти аналогичен объекту, сгенерированному в главе 2 средствами мастера Data Form Wizard. Этот код до бавляет в объект DataSet пару более утонченных параметров, а также задает свойства AutolncrementStep и AutoIncrementSeed столбца OrderlD объекта DataTable Orders, чтобы управлять значениями автоинкремента, которые ADO.NET генерирует для новых заказов. Кроме того, код задает значение свойства MaxLength столбцов со строковыми данными.
Код создает ограничение ForeignKeyConstraint, но не заполняет набор Relations объекта DataSet (подробнее об объекте DataRelation Ч в следующей главе).
Мастер Data Form Wizard создает объект DataSet со строгим контролем типов.
Подробно такие объекты обсуждаются в главе 9;
сейчас же рассматривайте объект DataSet со строгим контролем типов как класс, обладающий всеми возможностя ми DataSet и дополнительно предоставляющий структуры типа DataTable и Data Column, а также вполне определенные свойства вместо простых наборов. Следу ющий код создает объект DataSet без контроля типов:
Visual Basic.NET Dim ds As New DataSetQ Dim col As DataColumn Автономная работа с данными: объект DataSet модели ADO.NET 1 98 Часть III Dim fk As ForeignKeyConstraint 'Создаем таблицу customers With ds.Tables.Add("Customers") col =.Columns.Add("CustomerID", GetType(String)) col.MaxLength = col =.Columns.AddC'CompanyName", GetType(String)) col.MaxLength = col =.Columns.Add("ContactName", GetTypeCString)) col.MaxLength = col =.Columns.Add("Phone", GetType(String)) col.MaxLength =.PrimaryKey = New DataColumnO {.ColumnsC'CustomerlD")} End With 'Создаем таблицу orders With ds.Tables.AddC'Orders") col =. Columns,AddC'OrderlD", GetType(Integer)) col.AutoIncrement = True col.AutoIncrementSeed = - col.AutoIncrementstep = - col.Readonly = True col =.Columns.AddC'CustomerlD", GetType(String)) col.AllowDBNull = False col.HaxLength =.Columns.Add("EmployeeID", GetType(Integer)).Columns.Add("OrderDate", GetType(DateTime)}.PrimaryKey = New DataColumnO {.Columns("QrderlD")> End With 'Создаем таблицу order details With ds.Tables.Add("0rder Details").Columns.AddC'OrderlD", GetType(Integer)),Columns.AdcJC'ProductlD", GetType(Integer)).Columns.Add("UnitPrice", GetType(Decimal)) col.AllowDBNull = False col =.Columns.Add("Quantity", GetType(Integer)) col.AllowDBNull = False col.DefaultValue = "1" col =.Columns.Add("Discount", GetType(Decimal)) col.DefaultValue = "0",Columns.Add("ItemTotal", GetType(Decimal), "UnitPrice Х Quantity * (1 - Discount)").PrimaryKey = New DataColumnO {.Columns("OrderID"),.Columns{"ProductID")} End With 'Создаем ограничения ForeignKeyConstraints fk = New ForeignKeyConstraint(ds.Tables("Customers").ColumnsC'CustomerlD"), ds.Tables("Orders").ColumnsC'CustomerlD")) ГЛАВА 6 Работа с объектами DataSet ds.Tables("Orders").Constraints.Add(fk) fk = New ForeignKeyConstraint(ds.Tables("Orders").Columns("QrderID"), ds.TablesC"Order Details").Columns("OrderID")) ds.Tables("Order Details").Constraints.Add(fk) Visual C#.NET DataSet ds = new DataSetO;
DataTable tbl;
DataColumn col;
ForeignKeyConstraint fk;
//Создаен таблицу customers tbl = ds.Tables.Add("Customers");
col = tbl.Columns,Add{"CustomerID", typeof(string));
col.MaxLength = 5;
col = tbl.Columns.AddC'CompanyName", typeof(string));
eol.HaxLength = 40;
col = tbl.Columns.Add("ContactName", typeof(string));
col.MaxLength = 30;
col = tbl.Columns.AddC'Phone", typeof(string));
col.MaxLength = 24;
tbl.PrimaryKey = new DataColumn[] {tbl.Columns["CustomerID"]};
//Создаем таблицу orders tbl = ds.Tables.Add("0rders");
col = tbl.Columns.AddC'OrderlD", typeof(int));
col.AutoIncrement = true;
col.AutoIncrementSeed = -1;
col.AutoIncrementStep = -1;
col.Readonly = true;
col = tbl.Columns.AddC'CustomerlD", typeof(strtng));
col.AllowDBNull = false;
col.MaxLength = 5;
tbl.Columns.Add("EmployeelD", typeof{int));
tbl.Columns.AddC'QrderDate", typeof(DateTime));
tbl.PrimaryKey = new DataColumn[] {tbl,Columns["OrderID"]};
//Создаем таблицу order details tbl = ds.Tables.AddC'Order Details");
tbl. Columns. Add( "OrcterlD", typeof (int));
tbl.Columns.Add("ProductID", typeof(int));
col = tbl.Columns.Add("UnitPrice", typeof(Decimal));
col.AllowDBNull = false;
col = tbl.Columns.AddC'Quantity", typeof(int));
col.AllowDBNull = false;
col.OefaultValue = 1;
col = tbl.Columns,AddC'Discount", typeof(Decimal));
col.DefaultValue = 0;
tbl.Columns.Add("ItemTotal", typeof(Decimal), 200 Часть III Автономная работа с данными: объект DataSet модели ADO.NET "UnitPrice * Quantity * (1 - Discount)");
tbl.PrimaryKey = new DataColumn[] {tbl.Columns["OrderID"], tbl.Columns["ProductID"]};
//Создаем ограничения ForeignKeyConstratnts fk = new ForeignKeyConstraint(ds.Tables["CustO!ners"].Columns["CustornerID''], ds.Tables["Orders"].Columns["CustomerID"]);
ds,Tables["Orders"].Constraints.Add(fk);
fk = new ForeignKeyConstraint(ds.Tables["Orders"].Columns["OrderID"], ds.Tables["Order Details"].Columns["OrderlD"]);
ds.Tables["Order Details"].Constraints.Add(fk);
Изменение содержимого объекта DataTabie Вы уже умеете создавать объекты DataSet, DataTabie и DataColumn, помещать ре зультаты запросов в объекты DataTabie средствами объекта DataAdapter, а также просматривать содержимое объекта DataTabie. Теперь поговорим о том, как до бавлять, изменять и удалять объекты DataRow.
Добавление нового объекта DataRow Давайте заполним созданный нами объект DataSet данными. Б главе 5 рассказы валось, как с помощью объекта DataAdapter заполнюсь объект DataTabie данными из БД. Кроме того, можно загрузить данные из XML-файла;
подробнее об этом Ч в главе 12. Сейчас же я остановлюсь на построковой загрузке данных, У объекта DataTabie есть свойство Rows, возвращающее объект DataRowCoUection, который содержит набор объектов DataRow, Как и в случае с большинством на боров, для добавления новых элементов в объект DataRowCoUection годится ме тод Add. Тем не менее принципы создания объектов DataRow и других объектов ADO.NET различны.
Допустим, вам требуется программно добавить в объект DataTabie, уже содер жащий 10 объектов DataRow, еще 10 объектов DataRow. Чтобы добавить запись в таблицу, вы задаете значения каждого поля. Но как. DataRow узнает структуру таб лицы, т. е. опредетяет, какие столбцы она содержит? У объекта DataTabie есть метод NewRow, который возвращает новый объект DataRow с информацией обо всех столбцах таблицы, Visual Basic.NET Dim row As DataRow = ds.Tables("Customers").NewRow row("CustomerlD"} =: "ALFKI" Visual C#.NET Dataflow row = ds.Tables["Customers"].NewRow();
row["CustomerID"] =: "ALFKI";
Создав новый объект DataRow, стоит воспользоваться его свойством Item и заполнить различные поля. Кроме того, свойство Пет позволяет просмотреть содержимое поля записи. Item Ч свойство объекта DataRow по умолчанию, и по Работа с объектами DataSet ГЛАВА этому его не надо явно вызывать, чтобы им воспользоваться. Если нужно задать значение одного из полей объекта DataRow, укажите имя (или порядковый номер или собственно объект DataColumn) и требуемое значение поля.
Метод DataTableNewRow создает новую запись, однако не добавляет ее в объект DataTable. Вообще говоря, не следует моментально добавлять новую запись в таб лицу, поскольку в данный момент запись пуста. Полям задаются их значения по умолчанию или, если таковые не определены, значения Null. Если вы создадите новый объект DataRow и не станете сразу же добавлять его в набор Rows, то смо жете предварительно задать значения полей новой записи. Столбец CustomerlD таблицы Customers не принимает значений Null и не имеет значения по умолча нию. Предположим, у вас есть объект DataTable Customers с первичным ключом, основанным на столбце CustomerlD. Если вы попытаетесь добавить в таблицу новую запись Customers, не задав значение поля CustomerlD, система сгенерирует исклю чение.
Указав значения всех нужных полей записи, можно добавить ее в объект Data Table, воспользовавшись методом Add объекта DataRoivCollection и передав ему эту запись:
Visual Basic.NET Dim row As DataRow = ds.Tables("Customers").NewRow row("dJStonierID") = "ALFKI" ds.Tables("Customers").Rows.Add(row) Visual C#.NET DataRow row = ds.Tables["Customers"].NewRow();
row["CustoraerID"] = "ALFKI";
ds.Tables["Customers"].Rows.Add(row);
Объект DataTable предоставляет еще один способ добавить новую запись в таблицу Ч метод LoadDataRoic. Первый параметр данного метода Ч это массив значений, элементы которого соответствуют столбцам таблицы. Второй параметр, AcceptChanges, позволяет управлять значением свойства RowState нового объекта DataRow. Если передать в качестве этого параметра False (см. ниже), значением свойства RowState объекта DataRow будет Added, как и при добавлении новой за писи средствами методов DataTableNewRow и RowsAdd.
Visual Basic.NET Dim aValues As Object() = {"ALFKI", "Alfreds Futterkiste", "Maria Anders", "030-0074321"} ds.Tables("Customers").LoadDataRow(aValues, False) 8- 202 Часть III Автономная работа с данными: объект DataSet модели AOO.NET Visual C#.NET object[] aValues = {"ALFKI", "Alfreds Futterkiste", "Maria Anders", "030-0074321"};
ds.Tables["Customers"].LoadDataRowCaValues, false);
Когда вы передаете изменения в БД с помощью метода DataAdapterJUpdate, объект DataAdapter просматривает значение свойства RowState всех объектов DataRotc и определяет, как обновлять БД Ч изменять существующую, добавлять новую или удалять имеющуюся запись. Если передать в качестве второго параметра метода LoadDataRow значение True, значением свойства RoicState нового объекта DataRow будет Unmodified, Это означает, что запись не содержит отложенных из менений, которые DataAdapter должен передать в БД. Подробнее о свойстве RoivState объекта DataRow и обновлении БД Ч в главе 10.
Редактирование существующей записи Есть три способа программно изменить содержимое записи. Начнем с самого простого.
При работе с объектом DataRow значение поля можно задать, используя свой ство Item этого объекта. Ранее в этой главе я уже говорил, как посредством дан ного свойства просмотреть содержимое поля. Свойство доступно для чтения и записи, а значит, годится и для изменения значения поля. Следующий фрагмент кода с помощью метода Find набора Rows ищет запись в объекте DataTable Custo mers и затем изменяет значения полей CompanyName и ContactName. Подроб нее о методе Find Ч в главе 8. Сейчас же будем считать данный пример своеоб разным анонсом.
Visual Basic.NET Dim rowCustomer As DataRow rowCustomer = ds.Tables("Customers"),Rows.Find("ANTON") If rowCustomer Is Nothing Then 'Запись о клиенте не найдена!
Else rowCustomer("CompanyName"} = "NewCompanyName" rowCustomer("ContactName") = "NewContactName" End If Visual C#.NET DataRow rowCustomer;
rowCustomer = ds.Tables["Customers"].Rows.Find("ANTON"};
if (rowCustomer == null) //Запись о клиенте не найдена!
else { rowCustomer["CompanyName"] = "NewCompanyName";
rowCustomer["ContactName"] = "NewContactName";
I Второй способ обновления записи аналогичен первому за исключением того, что добавляются вызовы методов BeginEdit и EndEdit объекта DataRow.
Работа с объектами DataSet ГЛАВА б Visual Basic.NET Dim rowCustomer As DataRow rowCustomer = ds.Tables("Customers").Rows,FindC'ANTON") If rowCustomer Is Nothing Then 'Запись о клиенте не найдена!
Else rowCustome г.Beg inEdit rowCustomerC'CompanyName") = "NewCompanyName" rowCustomer("ContactName") = "NewContactName" rowCustomer.EndEdit End If Visual C#.NET DataRow rowCustomer;
rowCustomer = ds.Tables["Customers"].Rows.FindC'ANTON");
if (rowCustomer == null) //Запись о клиенте не найдена!
else { rowCustomer. BeginEditO;
rowCustomer["CompanyName"] = "NewCompanyName";
rowCustomer["ContactName"] = "NewContactName";
rowCustomer, EndEditO;
I Методы BeginEdit и EndEdit позволяют буферизовать изменения записи. При вызове EndEdit коррективы записи сохраняклся. Если вы решите отменить их, вызовите метод CancelEdit, и запись вернется в состояние на момент вызова Begin EdiL Есть еще одно отличие между двумя этими способами редактирования запи си. Объект DataTable предоставляет события RoivCbanging, Ron-Changed, Column Changing и ColwnnCbanged, с помощью которых удается просматривать измене ния записи или поля. Порядок наступления этих событий зависит от того, как вы изменяете запись Ч с вызовом методом BeginEdit и EndEdit или без него.
В первом примере содержимое записи менялось каждый раз при изменении содержимого одного из полей. События объекта DataTable наступают всегда, ког да вы изменяете содержимое поля. Если вызван метод BeginEdit, наступление со бытий откладывается до вызова EndEdit (если вы вызовете CancelEdit, буферизо ванные изменения отбрасываются, и поскольку запись не обновится, никакие события не наступают).
Третий способ Ч воспользоваться свойством ItemArray, Как и свойство Item, ItemArray позволяет просматривать и редактировать содержимое записи. Отличие их в том, что Пет рассчитано на одновременную работу с одним полем, a ItemArray принимает и возвращает массив, элементы которого соответствуют полям.
Visual Basic.NET Dim aCustomer As ObjectO = {"ALFKI", "NewCompanyName", _ "NewContactName", "NewPhoneNo"} Автономная работа с данными: объект DataSet модели ADO.NET 204 Часть III Dim rowCustomer As DataRow rowCustomer = ds. Tables("Customers").Rows.Find("ALFKI") rowCustomer. ItemArray = aCustomer Visual C#.NET object[] aCustomer = {"ALFKI", "NewCompanyName", "NewContactName", "NewPhoneNo"};
DataRow rowCustomer;
rowCustomer = ds.Tables["Customers"].Rows.Find("ALFKI");
rowCustomer,ItemArray = aCustomer;
Если вы хотите с помощью свойства ItemAiTay отредактировать содержимое лишь отдельных полей записи, воспользуйтесь ключевым словом Nothing (Visual Basic) или null (Visual C*.NET). Следующий фрагмент кода пропускает первое поле записи и изменяет содержимое второго, третьего и четвертого полей.
Visual Basic.NET Dim aCustomer As ObJectO = {Nothing, "NewCompanyName", _ "NewContactName", "NewPtioneNo"} Dim rowCustomer As DataRow rowCustomer = ds.Tables("Customers").Rows. Find("ALFKI") rowCustomer.ItemArray = aCustomer Visual C#.NET object[] aCustomer = {null, "NewCompanyName", "NewContactName", "NewPhoneNo"};
Dataflow rowCustomer;
rowCustomer = ds.Tables["Customers"].Rows.Find{"ALFKI">;
rowCustomer.ItemArray = aCustomer;
Примечание При изменении содержимого объекта DataRow содержимое со ответствующей записи БД не редактируется автоматически. Изменения содержимого объекта DataRow считаются отложенными, их можно позже передать в БД посредством объекта DataAdapter. Подробнее об этом Ч в главах 10 и 11.
Так каким же способом изменять записи объекта DataTablei Я предпочитаю вызвать BeginEditw EndEdit, поскольку при этом приходится писать четко струк турированный, удобочитаемый и простой в обслуживании код. Кроме того, в случае неожиданных проблем такой подход позволяет отменить сразу группу изменений записи.
Тем не менее, чтобы сократить примеры кода, в настоящей книге я практичес ки не использую методы BeginEditw EndEdit.
Работа со значениями Null в объекте DataRow При тестировании бета-версии.NET у разработчиков возникало множество во просов по поводу того, как задать полю записи БД значение Null или как прове ГЛАВА 6 Работа с объектами DataSet рить наличие в БД таких значений. Определить, содержит ли поле или запись значение Null, на самом деле очень просто.
У объекта DataRow есть метод IsNull, позволяющий проверить, содержит ли поле значение Null. Как и метод DataRowJtem, IsNull принимает имя поля, его порядко вый номер или объект DataColumn.
Ниже показано, как использовать метод IsNull объекта DataRow.
Visual Basic.NET Dim rowCustomer As DataRow rowCustomer = ds.Tables( "Customers"). Rows. Find("ALFKI") If rowCustomer. IsNull("Phone") Then Console. WriteLineC'It's Null") Else Console. WriteLineC'It's not Null") End If Visual C#.NET DataRow rowCustomer;
rowCustomer = ds.Tables["Customers"]. Rows, FindC'ALFKI");
if { rowCustomer. IsNull( "Phone")} Console. WriteLineC'It's Null");
else Console. WriteLineC'It's not Null");
Если вам нужно задать полю значение Null, не применяйте ключевое слово Null языков программирования. В пространстве имен System.NET Framework есть класс DBNull, свойство Value которого позволяет задавать полям записей значение Visual Basic.NET Dim rowCustomer As DataRow rowCustomer = ds.Tables("Customers"). Rows. FindC'ALFKI") rowCustomerC'Phone") = DBNull. Value Visual C#.NET Dataflow rowCustomer;
rowCustomer = ds.Tables["Customers"]. Rows. FindC'ALFKI");
rowCustomer[ "Phone"] = DBNull. Value;
Удаление объекта Удалить запись гораздо проще, чем изменить, Ч достаточно вызвать метод Delete объекта DataRow. Тем не менее удаленная запись.не исключается из структуры объекта DataTable. Вместо этого ADO. NET помечает запись, как ожидающую уда ления. Почему же ADO.NET не удаляет объект DataRow из таблицы?
Вспомните, что объекты для хранения данных в ADO.NET играют роль кэша данных, позволяющего получать данные из БД, редактировать их в автономном режиме и затем передавать в БД отложенные изменения. Вызывая метод, DataRow. Автономная работа с данными: объект DataSel модели ADO.NET 206 Часть III Delete, вы не удаляете соответствующую запись из БД. а просто помечаете запись как ожидающую удаления, чтобы позже удалось передать это отложенное изме нение в БД. Если полностью удалить объект DataRow из DataTable, при передаче отложенных изменений содержимого DataSet или DataTable соответствующая запись БД не будет удалена.
Подробнее о передаче отложенных изменений в БД Ч в главе 10.
Исключение объекта DataRow Чтобы по-настоящему удалить запись из объекта DataTable, а не просто пометить ее на удаление, воспользуйтесь методом Remove или RemoveAt класса DataRowCol lection, как показано ниже. Если у вас есть ссылка на нужный объект DataRow, используйте метод Remove. Если есть порядковый номер, вызывайте RemoveAt.
Visual Basic.NET Dim rowCustomer As DataRow rowCustomer = ds.Tables("Customers").Rows.Find("ALFKI") ds.Tablesf"Customers").RemoveC rowCustomer) ds.Tables("Customers").RemoveAt(intIndex) Visual C#.NET DataRow rowCustomer = ds.Tables["Customers"].Rows.FindC'ALFKI");
rowCustomer.ItemArray = aCustomer;
ds.Tables["Customers"].Remove(rowCustomer);
//или ds.Tables["Customers"].RemoveAt(intIndex);
Обычно я применяю метод Remove. Для вызова метода RemoveAt нужен поряд ковый номер объекта DataRow, а у DataRow нет свойств, которые возвращали бы такую информацию.
Кроме того, классы DataSet и DataTable предоставляют метод Clear, позволяю щий удалить все объекты DataRoiv без изменения структуры объекта DataSet или DataTable.
Использование свойства DataRow.RowState Объекты DataSet, DataTable и DataRou- работают, как автономный кэш данных. Вы можете выполнить запрос к БД и поместить его результаты в указанные объекты.
Ранее я рассказывал, как добавлять, изменять и удалять записи. Поскольку объек ты ADO.NET не соединены с БД, вносимые вами изменения не отразятся на со держимом БД. Конечно, автономное редактирование данных не особенно полез но, если позже сделанные изменения не удастся передать в БД.
ADO.NET поддерживает передачу изменений в БД. Подробнее об этой функ циональности Ч в главе 10. Сейчас же мы рассмотрим принципы поддержки данной функциональности объектом. DataSet. Чтобы кэшировать изменение объекта Data ГЛАВА 6 Работа с объектами DataSet Ron' для последующей передачи в БД, ADO.NET должна помнить, какое именно изменение вы сделали. Зачем, Ч спросите вы.
Один из способов обновления данных БД Ч выполнять командные запросы типа:
UPDATE HyTable SET FieldToHodify = NewValue WHERE PKField = PKValue AND FieldToHodify = OriginalValue INSERT INTO MyTable (Field!, Field2,.., FieldN) VALUES (Value!, Value2,... ValueN) или DELETE FROM MyTable WHERE PKField = PKValue Точно так же для обновления годятся и хранимые процедуры, Смысл в том, что логика для изменения записи отличается от логики для вставки записи и логики для удаления записи. Следовательно, для успешной передачи изменений в БД ADO.NET должна отслеживать, какое именно изменение вы со вершили в объекте DataRow.
ADO.NET хранит сведения о типе изменения в свойстве RowState объекта Data Roiv, которое принимает значения из перечисления DataRowState (табл. 6-1). Про смотрев значение данного свойства, вы сможете определить, была ли запись из менена, и если да, то как именно (отредактирована, вставлена или удалена), Таблица 6-1. Перечисление DataRowState Константа Значение Описание Запись не содержит отложенных изменений Unchanged 1 Запись не относится к объекту DataTable Detached 4 Запись добавлена в объект DataTable, но не существует в БД Added 16 Запись содержит отложенные изменения Modified 8 Запись ожидает удаления Deleted Глядя на список допустимых значений, вы можете решить, что свойство RowState способно возвращать комбинацию значений из перечисления DataRowState, од нако это не так Ч свойство всегда возвращает отдельное значение. В табл. 6- показано несколько случаев редактирования записи и приводятся результирую щие значения свойства RowState.
Таблтца 6-2. Примеры редактирования записи и результирующие значения RowState Значение RowState Пример Создание записи, не относящейся к объекту DataTable: Detached row = tbl.NewRow row("ColX") = "inltValue" Добавление новой записи в объект DataTable: Added tbl.Rows.Add(row) см. след. стр.
208 Часть III Автономная работа с данными: объект DataSet модели ADO.NET (продолжение) Таблтца 6-2.
Значение RowState Пример Получение записи: Unchanged row = tbl.Rows(O) Редактирование записи: Modified row.BeginEditO row("ColX") = "NewValuel" row.EndEdit() Удаление записи: Deleted row.Delete() Просмотр отложенных изменений объекта Dataflow Предположим, вы просмотрели содержимое объекта DataTable и с помощью свой ства RowState нашли измененную запись. Вы уже умеете просматривать содержи мое полей записи при помощи свойства Пет объекта DataRoiv. Кроме того, свой ство Item позволяет определить, каким было значение поля перед редактирова нием записи.
Свойство Item принимает второй необязательный параметр из перечисления DataRowVersion (табл. 6-3).
Таблица 6-3. Перечисление DataRowVersion Константа Значение Описание Текущее значение поля Current Original Оригинальное значение поля Proposed Предполагаемое значение поля (действительно только при редактировании записи с использованием BeginEdif) Команда по умолчанию Default Вообще говоря, есть две версии объекта DataRow Ч версия, представляющая текущее содержимое записи, и версия с исходным содержимым. Обычно для по иска записи необходимы оба этих набора сведений. Обновив запись, можно про сматривать как текущее, так и оригинальное содержимое полей. Следующий фраг мент кода изменяет содержимое поля CompanyName в объекте DataRow и затем возвращает текущее (новое) и оригинальное значения этого поля.
Visual Basic.NET Dim rowCustomer As DataRow rowCustomer = ds.Tables("Customers").Rows.FindC'ALFKI") rowCustomer("CompanyName") = "NewCompanyName" Dim strNewCompanyName, strOldCompanyName As String Console.WriteLine(rowCustomer("CompanyName", DataRowVersion.Current}) Console.WriteLine(rowCustomerf"CompanyName", DataRowVersion.Original)) ГЛАВА 6 Работа с объектами DataSet ' Visual C#.NET DataRow rowCustomer;
rowCustomer = ds,Tables["Customers"].Rows.Find("ALFKI");
rowCustomer["CompanyName"] = "NewCompanyName";
string strNewCompanyName, strOldCompanyName;
Console.WriteLire(rowCustomer["CompanyName", DataRowVe rsion.Cu rrent]);
Console.WriteLine(rowCustomer["CompanyName", DataRowVersion.Original]);
При редактировании записи с использованием методов BeginEditu End-Edit доступна еще одна версия DataRow Ч с предполагаемым содержимым. После вызова EndEdit все изменения сохраненяются в текущей версии записи. Тем не менее до этого изменения считаются отложенными, поскольку их удается отменить вызовом метода CancelEdit.
Чтобы при редактировании записи просмотреть предполагаемое значение поля, воспользуйтесь свойством Item объекта DataRow и задайте ему значение Proposed из перечисления DataRouVersion. При применении константы Current возвраща ется значение поля по состоянию на момент вызова метода BeginEdit (совсем не обязательно, что это значение Ч оригинальное).
Давайте рассмотрим различные состояния объекта DataRow и различные зна чения, возвращаемые свойством Item в зависимости от используемого значения DataRowVersion (это похоже на то, как законопроект превращается в закон, но без плавной анимации).
Б табл. 6-4 перечислены значения, возвращаемые свойством Item в зависимо сти от указанного значения из перечисления DataRowVersion, а также от текуще го состояния записи. Пометкой [Исключение] отмечены случаи, когда при вызове свойства Item со значением из перечисления DataRowVersion система генерирует исключение.
Таблица 6-4. Значения различных версий поля объекта DataRow Значение Ориги- Предпо- По умол чанию Текущее нальное лагаемое Пример [Исключение] [Исключение] NewValue InitiaWalue Только что созданная запись, не относящаяся к какому либо объекту DataTable-.
row = tbl.NewHow rowrColX") = "InitValue" [Исключение] [Исключение] InitialValue NewValut Добавление новой записи в объект DataTable:
tbl.Rows.Add(row) [Исключение] Retrieved Retrieved- Retrieved Только что полученная Value Value Value запись:
row = tbl.Rows(O) см. след, стр.
Часть 111 Автономная работа с данными: объект DalaSet модели ADO.NET Таблица 6-4. (продолжение) Значение По умол Ориги- Предпо чанию Текущее нальное лагаемое Пример При первом изменении Rettiered- Retriei'ed- NeivValue 1 NeuValue I [Ходержимого поля: Value Value row.BeginEditO row("ColX") = "NewValueV После первого изменения [Исключение] NewValuc I Retrieved- NewValue I Value содержимого поля:
row.EndEdit() При втором изменении NeicValue 1 Retrieved - NewValue содержимого поля: Value row.BeginEditf) rowfColX") = "NewValue2" После второго изменения Retrieved - [Исключение] NeivValue Value содержимого поля:
row.EndEditC) После отмены изменений: Neu'Value 2 Retrieved [Исключение] NeicValue Value row.BeginEditO row("ColX") = "ValueToCancel" row.CancelEditO [Исклю После удаления записи: [Исключение] [Исклю Retrieved чение] чение] Value row.DeleteO Примечание При успешном редактировании изменяется текущее, но не за трагивается оригинальное значение. При вызове метода CancelEdit вос станавливается значение поля по состоянию на момент вызова метода BegmEdit (совсем не обязательно, что это значение Ч оригинальное).
Если вы попытаетесь просмотреть текущие значения полей удален ной записи, система сгенерирует исключение. Тем не менее можно сво бодно просматривать исходное содержимое таких записей.
Мы обсудили три из четырех значений, входящих в перечисление DataRotv Version. Теперь я расскажу о значении Default Если задать его свойству RowState, Item не вернет значение поля по умолчанию. За возврат такого значения отвеча ет свойство DefaultValue. Значение Default из перечисления DataRowVersion Ч это просто значение по умолчанию параметра, принимаемого свойством Item объек та DataKow, Как я уже говорил, свойство Item возвращает текущее значение поля записи.
Точность данного утверждения зависит от того, что понимать под словом текущее.
Работа с объектами DalaSet ГЛАВА 6 Если вы не редактируете запись, вызов свойства Item без необязательного па раметра равносилен тому, как если бы вы вызвали это свойство и передали в ка честве параметра константу DataRoivVersion.Current. Тем не менее при редактиро вании записи метод Item, вызванный без необязательного параметра, вернет пред полагаемое значение поля.
Работа с объектами DataSet в среде Visual Studio.NET Вы уже достаточно знаете о структуре объектов DataSet и о том, как создавать та кие объекты программно. Но вот задача: при этом приходится писать много кода!
Рассмотрим некоторые возможности среды разработки Visual Studio.NET, значи тельно упрощающие создание объектов DataSet.
Генерирование объекта DataSet на основе объектов DataAdapter Как уже говорилось, структура объекта DataSet обычно зависит от структуры дан ных, возвращаемых объектами DataAdapter, и средствами этих объектов можно формировать структуру объекта DataSet Visual Studio.NET также позволяет создавать объекты DataSet на основе объектов DataAdapter.
При работе с конструктором, например Windows- или Web-формой, содержа щим объекты DataAdapter, на основе этих объектов создаются объекты DatuSet.
Выберите в меню Data команду Generate Dataset, щелкните в окне Properties ссылку Generate Dataset или щелкните область проектирования правой кнопкой и вы берите Generate Dataset (рис. 6-1). Откроется диалоговое окно Generate Dataset (рис. 6-2).
Рис. 6-1. Открытгге диалогового окна Generate Dataset Часть N1 Автономная работа с данными: объект DataSet модели ADO.NET <5enetate a datasst that Deludes the specified tables, Choose & dataset:
Choose whfcrt tablets) to add to pi/Customers (dEbDataAdapte iji/ Orders (QleDbDataAdapterZ) ftddlhisdataset tolhecteiigriei-.
НЫр Рис. 6-2. Диалоговое окно Generate Dacaset В этом диалоговом окне создают новый или изменяют структуру существую щего объекта DataSet. Кроме того, здесь можно указать объекты DataAdapter, на основе которых будет создан объект DataSet. В диалоговом окне Generate Dataset (рис. 6-2) отображаются как имя объекта DataAdapter, так и имя таблицы, на ко торую он ссылается. Это позволяет легко отобрать нужные объекты DataAdapter для создания объекта DataSet. Кроме того, в диалоговом окне Generate Dataset есть флажок, позволяющий добавитыэкземпляр нового объекта DataSet в конструктор.
Добавленный экземпляр объекта DataSet отобразится в панели компонентов области проектирования. На рис. 6-3 я создал новый объект DataSet и добавил его экземпляр в конструктор. Если выбрать этот экземпляр, в окне Properties отобра зятся его свойства.
Заметьте: в окне Solution Explorer появится новый файл с расширением.xsd.
Имя этого файла соответствует имени нового объекта DataSet. Откуда он взял ся?* Ч спросите вы. Visual Studio.NET создает новый объект DataSet, вызывает метод FillSchema объектов DataAdapter, указанных в диалоговом окне Generate Dataset, и затем вызывает метод WnteXmlScbema этого объекта DataSet, чтобы сохранить его информацию схемы в файл.
Примечание Данная функция среды Visual Studio.NET демонстрирует, насколько полезен метод типа DataAdapterfillSchema, когда его используют надле жащим образом.
Рассмотрим.xsd-файл, созданный нами при помощи диалогового окна Generate Dataset. Дважды щелкните значок файла в окне Solution Explorer Ч откроется кон структор XML-схем Visual Studio.NET (рис. 6-4);
.xsd-файл содержит обычную XML схему с несколькими дополнительными, специфичными для объекта DataSet, ат рибутами. Объекты DataTable представлены в виде вложенных элементов объек ГЛАВА 6 Работа с объектами DataSet та DataSet, а объекты DataColumn Ч в виде вложенных элементов объектов Data Table. Если в конструкторе, представляющем DataSet, щелкнуть объект DataTable или DataColumn, в окне Properties отобразятся их свойства.
да-й'йнв -:^ JjJ^uflontfs. i[CusCornfr<) WindotHAppllcMkilil Й" : ft- Сивйтят Япггд il R^er^fKEb i ^ A^ssnblvJnfo.vb i OiivactName sfting L Phone string 50**! Q Рис. 6-4. Содержимое файла схемы нового объекта DataSet Автономная работа с данными: обьект DataSet модели ADO.NET 214 Часть III Создание нового объекта DataSet с нуля Что, если вам нужно создать объект DataSet, по у вас нет объектов DataAdapter, чтобы определить его структуру? Воспользуйтесь диалоговым окном Add New Item (рис. 6-5) и добавьте в проект.xsd-файл объекта DataSet. A d d New (tern - WindowsApplicafienl templates: Canpcnent> Добавив в проект объект DataSet, вы увидите такой же конструктор, как при создании DataSet на основе объектов DataAdapter, за исключением того, что объект DataSet окажется пуст. Давайте добавим в этот DataSet новый объект DataTable. Для этого щелкните конструктор правой кнопкой и выберите Acld\New Element (рис. 6-6). Можно также указать одноименную команду в меню Schema. Примечание ADO.NET способна хранить сведения схемы DataSet в формате XML-схемы, так что вполне естественно будет просматривать и изменять структуру объектов DataSet средствами конструктора XML-схем. К сожа лению, данный способ имеет некоторые недостатки. Знакомые с XML разработчики быстро поймут, что объекты DataTable, входящие в состав DataSet, соответствуют элементам файла схемы, а разработчиков, не имеющих опыта работы с XML, этот факт может смутить. Вероятно, в будущих версиях Visual Studio.NET появится конструктор объектов Data Set, предоставляющий более гибкие возможности работы с данными. Сейчас же я вкратце расскажу, как создавать новые объекты DataSet при помощи имеющегося интерфейса. ГЛАВА 6 Работа с объектами DataSet I E. NewglsmafiE '. ; 'A"; New attribute I ~t~f New cranplexType ^f New srnpleType и New group ^, New attributeuroup Х<Х; New any ^ft; New anv.Attribute ! Generate Dataset i Preview Dataset... Рис. 6-6. Добавление нового объекта DataTable в объект DataSet В новый объект DataTable сразу следует добавить несколько объектов Од taColumn. В конструкторе XML-схемы щелкните левую ячейку первой записи блока, представляющего ваш объект DataTable. Справа от ячейки должна появиться стрелка. Если щелкнуть ее, появится список элементов, которые разрешено добавить в объект DataTable (рис. 6-7). Чтобы добавить новый объект DataColumn, выберите в списке пункт Element и задайте имя добавляемого объекта. Введенный вами текст будет задан свойству ColumnName объекта DataColumn, Рис. 6-7. Добавление нового объекта DataColumn в объект DataTable Чтобы задать другие свойства объекта DataColumn, щелкните в конструкторе XML-схемы нужный столбец. На рис. 6-8 показана часть свойств, доступных в окне Properties после выбора столбца в конструкторе. Заметьте: одни свойства специ фичны для объекта DataColumn, а другие относятся скорее к XML-элементам. Некоторые свойства, добавленные на последних этапах создания объектной мо дели ADO.NET, например свойство MaxLength объекта DataColumn, в окне Properties недоступны. Создав новый объект DataSet с требуемой структурой, сохраните изменения и закройте конструктор XML-схемы. После этого в проект будет добавлен готовый файл схемы объекта DataSet, Чтобы добавить экземпляр DataSet в область проек тирования, выберите на вкладке Data панели инструментов Visual Studio.NET ком понент DataSet, как если бы хотели добавить в конструктор другой компонент, например кнопку. Дважды щелкните этот компонент или перетащите его в область проектирования или в ее панель компонентов. Откроется диалоговое окно Add Dataset (рис. 6-9), Часть III Автономная работа с данными: объект DataSet модели ADO.NET ''- Windomftpplitalniil Мн.чивП raual Basic.N11 | feijMi j Data:el2.art* Рис. 6-8. Задание свойств нового объекта DataColumn Choose a typed or tnt^ed dataset to add to the designer. <Х Typed dateset Name : Wi ndo w s Applit a tio n 1, Da t a Stt Oeates an instance of a typed dateset da; s already m your project. Cheose tivs арйоп to work vлth a dataset that has a hult^n schema, See Heb for detafc on generating typed daEasets. ' Untyped dataset Creates an instance of an untyped dataset> Cfiaose this opnonwhen you want a dataset Щth no Khema. Help Рис. 6-9. Добавление экземпляра нового объекта DataSet В нем можно выбрать любой из входящих в проект файлов схемы объектов DataSet. Просто укажите в списке нужный объект DataSet и щелкните ОК Ч в кон структор будет добавлен новый экземпляр вашего объекта DataSet. Заметьте: если нужно добавить экземпляр нового объекта DataSet программ но, DataSet доступен как определенный в проекте новый класс. На рис. 6-10 в модуль кода Visual Basic.NET добавляется экземпляр нового объекта DataSet. ГЛАВА 6 Работа с объектами DataSet л WiadBWsAppScflliiml tticEtftofl Vfeunia В диалоговом окне Add Dataset (рис. 6-9) имеется переключатель, позволяю щий создать объект DataSet без контроля типов. Посмотрим, как этим переклю чателем воспользоваться. Для начала воспользуйтесь компонентом DataSet с панели инструментов Visual Studio.NET и добавьте в конструктор еще один объект DataSet. Когда откроется диалоговое окно Add Dataset, щелкните переключатель Untyped Dataset и затем Ч ОК. В панели компонентов конструктора появится новый объект DataSet. Щелкните этот объект Ч в окне Properties отобразятся его свойства. Чтобы добавить объекты DataTable, выберите свойство Tables и щелкните расположен ную справа кнопку (...)Х Запустится редактор Visual Studio.NET Collection Editor. Его используют многие компоненты из состава Visual Studio.NET. На рис. 6-11 пока зано, как этот редактор выглядит при работе с объектами DataTable, входящими в состав DataSet. Редактор Collection Editor также применяется для добавления объектов Data Column в объекты DataTable. Чтобы добавить объект DataColumn, выберите в Collec tion Editor нужный объект DataTable. После этого щелкните в списке справа на бор Columns и затем Ч кнопку (...). Откроется еще один редактор Collection Editor (рис. 6-12), предназначенный для создания объектов DataColumn. Часть III Автономная работа с данными: объект DataSet модели ADO.NET '- Tables Collection Editor Table 1 Properties Column; (Collection) Constraints (Collection) DisplayExpressio, Minimum Capacity i Namespace : Prefix fPrmaryKey DataCoLrnn[] hihlr i DataTable I Friend dose Help Рис. 6-11. Добавление нового объекта DataTable в объект DataSet без контроля типов Columns Collection Frtilor Column I Pj ullowDBNul True AutiS Increment Fal^e AijtolncrementSf С Caption Column L uluninl DataType 5/stem, String Def aufcvalue I Express:on SMaxLength [... ni^'.... I Prefix ! Readonly Рис. 6-12. Добавление нового объекта DataColumn в объект DataTable Добавив объекты DataColumn, можно определить первичный ключ объекта DataTable. Выбрав в Collection Editor объект DataTable, is списке справа вы увиди те свойство PrimaryKey. Щелкните его, а затем Ч стрелку справа от него. Появит ся список столбцов объекта DataTable (рис. 6-13). Выберите объект DataColumn, который станет первичным ключом объекта DataTable и затем щелкните любое свободное место редактора Collection Editor. Работа с объектами DataSel ГЛАВА Customers Properties: 13- - -. -' '" !Ш (DynarmcProoe't щ -. Columns ( Collect i on) : CorstiEwts (Collection) Uf OisplayExpressio r MirihiumCapacity Ш Namespace > - : Prefix ~J Data Col umn[] ' TableName Add Help Рис. 6-13. Определение первичного ключа объекта DataTable Кроме того, можно добавлять элементы в наборы Constraints ваших объектов DataTable. Щелкните в редакторе Collection Editor свойство Constraints объекта DataTable и затем Ч кнопку (...) справа от него. Откроется еще один редактор Collection Editor (рис. 6-14), позволяющий изменять содержимое набора Constraints объекта DataTable. Если первичный ключ объекта DataTable уже определен, вы увидите, что набор Constraints уже включает один элемент. Constraint* Collection tditof Ccnstrahtl Properties Add -r 1 dit i Remove Unique Constraint Help Рис. 6-14. Добавление ограничений в объект DataTable Редактор Collection Editor позволяет добавлять ограничения UniqueConstraint и ForeignKeyConstraint. Если щелкнуть кнопку Add, появится контекстное меню, Часть III Автономная работа с данными: объект DataSet модели ADO.NET предлагающее выбрать нужный тип ограничения. На рис. 6-15 показан интерфейс для добавления ограничений UniqueConstraint. Он очень прост Ч вам достаточно лишь выбрать объект DataColumn, добавляемый в состав нового уникального ключа. Кроме того, можно указать имя ключа и сообщить, должен ли он стать первич ным ключом объекта Data-Table. Unique Constraint tome: fconstraint; CompanyName ContactName i CustomsrtD Г~ ft-irnarytey Help Рис. 6-15. Добавление нового ограничения UniqueConstraint в объект DataTable На рис. 6-16 показан интерфейс для добавления ограничений ForeignKeyConst raint. Он не требует дополнительных пояснений, так как практически идентичен интерфейсу, средствами которого мы в главе 2 определяли отношение между таб лицами Customers и Orders из состава DataSet, Fo reign Key Constrain! Рис. 6-16. Добавление нового ограничения ForeignKeyConstraint в объект DataTable ГЛАВА 6 Работа с объектами DataSel Особенности объектов DataSet, DataTable, Data Column, DataRow, UniqueConstraint и ForeignKeyConstraint Вы уже получили представление о базовых возможностях объекта DataSefvi свя занных с ним объектов, а сейчас я детально расскажу обо всех предоставляемых этими объектами свойствах, событиях и методах. Свойства объекта DataSet В табл. 6-5 перечислены наиболее часто используемые свойства объекта DataSet, Таблица 6-5. Свойства объекта DataSet Тип данных Описание Свойство Определяет, различается ли регистр симво Boolean CaseSensitive лов при сравнении строк Определяет имя объекта DataSet DatoSetName String Указывает, находится ли объект DataSet в ре Boolean DesignMode жиме проектирования Определяет, обеспечивает ли DataSet выпол EnforceConstraints Boolean нение определенных на нем ограничений Содержит набор динамических свойств и ExtendedProperties PropertyCollection значений Указывает, содержит ли DataSet ошибки HasErrors Boolean Определяет региональные параметры, ис Locale Cultttrelnfo пользуемые объектом DataSet при сравне нии строк Содержит пространство имен, которое String Namespace ADO.NET использует при записи содержимо Ф го DataSet в XML-файл или при загрузке XML-данных в объект DataSet Содержит префикс пространства имен, при Prefix Siring меняемого ADO.NET при записи содержимо го DataSet в XML-файл или при загрузке XML-данных в объект DataSet DataRelationCottection Содержит набор объектов DataRelation. вхо Relations дящих в состав DataSet Содержит набор объектов DataTable, входя Tables DataTableCollection щих в состав DataSet Свойство CaseSensitive Свойство CaseSensitive объекта DataSet определяет, различается ли регистр сим волов при сравнении строк в объекте DataSet. Значение данного свойства по умол чанию Ч False. Если изменить значение свойства CaseSensitive объекта DataSet, аналогичным образом откорректируется значение свойства CaseSensitive всех объектов DataTable из состава этого DataSet, для которых значение данного свойства не было задано. Объект DataTable также предоставляет свойство CaseSensitive. Автономная работа с данными: объект DataSet модели ADO.NET 222 Часть III Свойство DataSetName Свойство DataSetName содержит имя объекта DataSet. Его значение можно задать в конструкторе DataSet. Если значение опущено, свойству DataSetName автомати чески задается значение NewDataSet. При записи содержимого DataSet u XML-документ свойство DataSetName оп ределяет имя корневого элемента этого документа. Кроме того, данное свойство также определяет имя класса, генерируемого при создании средствами утилиты XSD.exe файла класса на основе файла XML-схемы. Свойство DesignMode Свойство DesignMode возвращает логическое значение, указывающее, находится ли объект DataSet в режиме проектирования. Это свойство полезно при написа нии кода в нестандартном элементе управления. Если объект DataSet использует ся в компоненте в период разработки, свойство DesignMode возвращает True. В противном случае оно возвращает False. Объект DataTable также предоставляет свойство DesignMode. Оно доступно толь ко для чтения. Свойство EnforceConstraints Свойство EnforceConstraints определяет, обеспечивает ли объект DataSet выполнение определенных на нем ограничений. Значение свойства данного свойства по умол чанию Ч True. Если нужно временно отключить ограничения, задайте свойству EnforceConstraints значение False. Если вы задали свойству EnforceConstraints значение True и текущее содержи мое DataSet нарушает какие-либо из назначенных ограничений, ADO.NET сгене рирует исключение ConstraintException. Свойство ExtendedProperties Свойство ExtendedProperties объекта DataSet позволяет хранить различную инфор мацию. Оно возвращает объект Property Collection, предназначенный для хранения разнообразных объектов. Несмотря на то. что свойство ExtendedProperties предо ставляет довольно большие возможности, рекомендую вам ограничиться хране нием простых строк. Когда вы сохраняете содержимое схемы объекта DataSet как файл или поток, ADO.NET записывает содержимое набора ExtendedProperties в виде строк. Объекты DataTable, DataColumn, DataRelation и Constraint также предоставля ют свойство ExtendedProperties. Далее показано, как добавить записи в набор ExtendedProperties объекта DataSet и как обращаться к содержимому этого набора: Visual Basic.NET Dim ds As New DataSetO 'Добавляем дополнительные свойства ds.ExtendedProperties,Add("Prop1", "Valuer1) ds.ExtendedProperties.Add("Prop2", "Value2") ds.Extended?roperties.Add{"Prop3", "Values") Работа с объектами DataSet ГЛАВА 'Получаем значение дополнительного свойства Console.WriteLlneCds.Extended?гореrties("Prop2")) 'Получаем и перечисляем все расширенные свойства Dim objEnum As IDictionaryEnumerator objEnum = ds.ExtendedProperties.GetEnumerator Do While objEnum.MoveNext Console.WriteLine(objEnum,Key & " = " & objEnum.Value) Loop Visual C#.NET //Добавьте "using System.Collections" DataSet ds = new DataSet(); //Добавляем дополнительные свойства ds.ExtendedProperties.Add("Prop1", "Valuel"); ds.Extended?roperties.Add("Prop2", "Value2"); ds.Extended?roperties.Add("РгорЗ", "Values"); //Получаем значение дополнительного свойства Console.WriteLine(ds.ExtendedProperties["Prop2"]); //Получаем и перечисляем все дополнительные свойства IDictionaryEnumerator objEnum; objEnum = ds. ExtendedProperties.GetEnumeratorO; while (obj Enurn. MoveNext()) Console.WriteLine(objEnum.Key + " = " + objEnum.Value); Свойство HasErrors Свойство HasErrors возвращает логическое значение, указывающее, содержат ли объекты DataRoic, входящие в состав DataSet, ошибки. Если вы передаете в БД пакеты изменений и задали свойству ContinueUpdateOnError объектов DataAdapter значение True, проверяйте по завершении передачи значение свойства HasErrors объектов DataSet. Таким образом вы узнаете, все ли операции передачи изменений завер шились успешно, Объекты DataTable и DataRow также предоставляют свойство HasEn~ors. Подробнее о действиях в случае ошибок при передаче изменений в БД Ч в главе 11. Свойство Locale В различных языках действуют разные правила сравнения строк. По умолчанию объект DataSet сравнивает строки, используя текущие региональные параметры системы. Изменить это поведение удается при помощи свойства Locale объекта DataSet. Данное свойство принимает объект Culturelnfo, относящийся к пространству имен System.Globalization. Подробнее об объекте Culturelnfo Ч в документации MSDN, Если изменить значение свойства Locale объекта DataSet, аналогичным обра зом изменится значение свойства Locale всех объектов DataTable из состава это го DataSet, для которых значение данного свойства не было задано. 224 Часть 111 Автономная'работа с данными: объект DataSet модели ADO.NET Объект DataTable также предоставляет свойство Locale. Следующий фрагмент кода задает свойству Locale объекта DataSet значение English (Australia): Visual Basic.NET Dim ds As New DataSetO ds.Locale = New System.Globalization.CultureInfo("en-AU") Console. WriteLinefds.Locale.DisplayName) Visual C#.NET DataSet ds = new DataSetQ; ds.Locale = new System.Globalization,CultureInfo("en-AU"); Console.WriteLlne(ds.Locale.DisplayName}; Свойства Namespace и Prefix Свойства Namespace и Рге/гх позволяют задать для объекта DataSet префикс и пространство имен XML. ADO.NET использует значения этих свойств при записи содержимого DataSet в XML-файл, а также при загрузке XML-документа в объект DataSet. Объекты DataTable и DataRow также обладают свойствами Namespace и Prefix. Подробнее о пространствах имен XML Ч в документации MSDN, Свойство Relations Свойство Relations возвращает объект DataRelationCottection, содержащий входя щие в объект DataSet объекты DataRelation. Данное свойство позволяет просмат ривать, добавлять, изменять и удалять объекты DataRelation. Свойство Tables Свойство Tables позволяет просматривать, добавлять, изменять и удалять объекты DataRelation. Оно возвращает объект DataTableCollection, содержащий входящие в объект DataSet объекты DataTable. Обращаться к объектам DataTable через свойство Tables разрешается по их имени (свойство TableName) или порядковому номеру. В последнем случае производи тельность выше. Методы объекта DataSet В табл. 6-6 перечислены наиболее часто используемые методы объекта DataSet. Таблица 6-6. Методы объекта DataSet Метод Описание Подтверждает все отложенные изменения в объекте DataSet AcceptCbanges Вызывается конструкторами Visual Studio.NET перед добавлением в Beginlnit объект DataSet сведений схемы Удаляет из DataSet все объекты DataRow Clear Создает новый объект DataSet с идентичной схемой, но без объектов Clone DataRow Работа с объектами DataSel ГЛАВА (продолжение) Таблица 6-6. Метод Описание Создает новый объект DataSet с такой же схемой и объектами DataRow Сору Вызывается конструкторами Visual Studio.NET после добавления в Endlnit объект DataSet сведений схемы Возвращает новый объект DataSet с идентичной схемой, содержащий GetCbanges измененные записи оригинального объекта DataSet Возвращает содержимое объекта DataSet в виде XML-строки GetXtnl Возвращает схему объекта DataSet в виде XML-строки GetXmlSchema Возвращает логическое значение, указывающее, содержат ли объекты HasChanges DataRow из состава DataSet отложенные изменения InferXmlScbema Загружает информацию схемы из XML-схемы и позволяет указать спи сок пространств имен, элементы которых следует исключить из схемы объекта DataSet Осуществляет слияние данных из другого объекта DataSet, DataTable Merge или массива объектов DataRow и данных текущего объекта DataSet Загружает XML-данные в объект DataSet из файла, объекта Stream, ReadXml TextReader или XmlReader ReadXmtScbema Загружает информацию XML-схемы в объект DataSet из файла, объекта Stream, TextReader или XmlReader Отменяет все отложенные изменения в объекте DataSet RejectCbanges Восстанавливает оригинальное состояние объекта DataSet, в котором Reset он находился до инициализации Записывает содержимое объекта DataSet в XML-формате в файл, объект WriteXml Stream. TextReader или XmlReader WriteXmlSchema Записывает схему объекта DataSet в XML-формате в файл, объект Stream, TextReader или XmlReader Методы AcceptChanges и RejectChanges Методы AcceptChanges и RejectCbanges позволяют подтверждать и отбрасывать нее отложенные изменения в объекте DataSet. Когда вы редактируете содержимое объекта DataRow, ADO.NET помечает этот объект как содержащий отложенное изменение и задает его свойству RowState соответствующее значение Ч Added, Modified или Deleted, Кроме того, ADO.NET сохраняет оригинальное и текущее содержимое объекта DataRow. При вызове метода AcceptChanges объекта DataSet ADO.NET подтвердит все отложенные изменения в объектах DataRow из состава вашего DataSet, Свойству RowState всех записей, у которых его текущее значение Ч Added или Modified, за дается значение Unchanged. При этом ADO.NET также сбросит лоригинальные значения полей объектов DataRow\ теперь лоригинальным станут текущие зна чения этих полей. После вызова метода AcceptChanges из объекта DataSet удаля ются все объекты DataRow, значение свойства RowState которых Ч Delete. Успешно передав отложенные изменения, хранящиеся в объекте DataRow, объект DataAdapter неявно вызывает метод DataRoivAcceptChanges. При вызове метода RejectChanges все отложенные изменения в объекте DataSet отбрасываются и из него удаляются все объекты DataRow, значение свойства Автономная работа с данными: объект DataSet модели ADO. NET 226 Часть III RoivState которых Ч Added. Если значение свойства RowState объекта DataRou' Ч Modified или Deleted, восстанавливается оригинальное состояние этого объекта. Объекты DataTable и DataRow также обладают методами AcceptCbanges и Reject Changes. Методы Beginlnit и Endlnit Методы Beginlnit и Endlnit вызываются конструкторами, и использовать их непос редственно в коде не требуется. Если в период разработки вы средствами конст рукторов Visual Studio.NET создали объект DataSet без контроля типов, то заме тите, что конструктор сгенерировал код, использующий данные методы. Код вы зывает метод DataSetJSeginlnit, добавляет в объект DataSet информацию схемы и затем вызывает метод Endlnit. Я пытался использовать эти методы в коде, полагая, что смогу помочь вам понять, что же они делают, однако так и не обнаружил ни одного случая (например до бавление столбца, основанного на выражении, до добавления столбца, на кото рый первый столбец ссылается), который еще не был описан. Объект DataTable также обладает методами Beginlnit и Метод Clear Метод dear позволяет удалить из объекта DataSet все объекты DataRou: Вызвать его быстрее, чем освободить оригинальный и создать новый DataSet с идентич ной структурой. Объект DataTable также предоставляет метод Clear. Методы Clone и Сору Метод Сору позволяет создать новый объект DataSet, по структуре и содержимо му аналогичный оригинальному. Чтобы создать объект DataSet с идентичной струк турой, но без записей, воспользуйтесь методом Clone. Объект DataTable также обладает методами Clone и Сору. Метод GetChanges Метод DatoSet.GetCbanges возвращает новый объект DataSet со структурой ориги нального объекта DataSet, содержащий все записи оригинального объекта DataSet с отложенными изменениями. Подробнее об этом методе Ч в главе 1 1, Объект DataTable также предоставляет метод GetChanges. Примечание Для соответствия ограничениям ссылочной целостности, опре деленным в оригинальном объекте DataSet, новый DataSet может также включать несколько неизмененных объектов DataRow. Если вы измени ли дочернюю запись, а родительскую не трогали, в новый DataSet вклю чается именно родительская запись. Если бы новый объект DataSet со держал дочернюю запись, это нарушило бы ограничение ссылочной целостности. ГЛАВА 6 Работа с объектами DataSet Методы GetXml и GetXmlSchema Метод GetXml позволяет получить содержимое объекта DataSet, в том числе ин формацию схемы, в виде строки XML-формата. Если же вам требуется только информация схемы, воспользуйтесь методом GetXmlSchema. Подробнее о функциях ADO.NET для работы с XML-данными Ч в главе 12. Метод HasChanges Метод HasChanges возвращает логическое значение, указывающее, есть ли в объекте DataSet объекты Data-Row с отложенными изменениями, Рекомендую вам использовать данный метод при создании приложений, по зволяющих изменять содержимое DataSet и передающих сделанные изменения в БД при помощи объектов DataAdapter. Какой смысл пытаться передать отложен ные изменения из DataSet в БД, если их нет? Метод Merge Метод Merge позволяет загрузить в имеющийся объект DataSet данные из другого объекта DataSet, DataTable или массива объектов DataRow. Подробнее о методе Merge Ч в главе 11. Методы ReadXml и WriteXml Метод ReadXml позволяет загрузить в объект DataSet XML-данные из файла, объекта TextReader, Stream или XmlReader. Для управления порядком считывания XML-дан ных применяют параметр режим. Он принимает значения из перечисления XmlReadMode и позволяет указать, нужно ли считывать весь XML-документ или только XML-фрагмент и требуется ли считывать XML-схему. У объекта DataSet также есть метод WriteXml, позволяющий записывать его содержимое XML-формате. Данный метод предоставляет те же параметры, что и метод ReadXml Подробнее о функциях ADO.NET для работы с XML-данными Ч в главе 12. Методы ReadXmlSchema, WriteXmlSchema и InferXmlSchema Методы ReadXmlSchema и WriteXmlSchema аналогичны методам ReadXml и WriteXml, но предназначены для работы с XML-схемами. Как и ReadXml и WriteXml, они принимают объект TextReader, Stream, XmlReader или строку с именем файла с XML данными. Метод InferXmlSchema похож на ReadXmlSchema, но предоставляет расширен ные возможности управления Ч он позволяет указать пространства имен, элементы которых следует игнорировать. Подробнее об этом Ч в разделе Loading DataSet Schema Information from XML документации MSDN. Подробнее о функциях ADO.NET для работы с XML-данными Ч в главе 12. Метод Reset Метод Reset восстанавливает оригинальное состояние объекта DataSet, в котором он находился до инициализации. Чтобы отбросить имеющийся и начать работу с новым объектом DataSet, используйте метод Reset, а не создавайте новый экзем пляр DataSet, Автономная работа с данными: объект DataSet модели ADO.NET Часть События объекта DataSet В табл. 6-7 указано наиболее часто используемое событие объекта DataSet. Таблица 6-7. Событие объекта DataSet Событие Описание Наступает, если метод DataSetMerge сгенерировал исключение MergeFailed Событие MergeFailed Событие MergeFailed позволяет обрабатывать любые ошибки, возникающие при использовании метода Merge объекта DataSet. Лично я так и не смог заставить это событие наступить. Возможно, у вас это получится. Свойства объекта DataTable В табл. 6-8 перечислены наиболее часто используемые свойства объекта DataTable. Таблица 6-8. Свойства объекта DataTable Свойство Тип данных Описание Определяет, различается ли регистр симво CoseSensitive Boolean лов при сравнении строк Возвращает объекты DataRelation с дочер ChildRelations DataRelationCollection ними данными объекта DataTable Columns DalaColu mnCottection Содержит набор объектов DataColumn, входящих в состав объекта DataTable Constraints ConstraintCollection Содержит набор объектов Constraints, вхо дящих в состав объекта DataTable DataSet DataSet Возвращает объект DataSet, в состав кото рого входит DataTable DefaultView DataView Возвращает объект DataView, который свя занные элементы управления получают от объекта DataTable DesignMode Boolean Указывает, находится ли объект DataTable в режиме проектирования Содержит набор динамических свойств и ExtendedProperties PropertyCollection значений Boolean Указывает, содержит ли DataTable ошибки HasEt-rors Locale Culturelnfo Определяет региональные параметры, ис пользуемые объектом DataTable при срав нении строк Namespace String Содержит пространство имен, которое ADO.NET использует при записи содержи мого DataTable в XML-файл или при загруз ке XML-данных в объект DataTable ParentRelations DataRelationCollection Возвращает объекты DataRelation с роди тельскими данными объекта DataTable Prefix String Содержит префикс пространства имен, ис пользуемого ADO.NET при записи содер жимого DataTable в XML-файл или при за грузке XML-данных в объект DataTable ГЛАВА 6 Работа с объектами DataSet (продолжение) Таблица 6-8. Тип данных Описание Свойство Массив объектов Содержит информацию о первичном PrimaryKey ключе объекта DataTable DataColumn Содержит набор объектов DataColumn, DataRowCollection Rows входящих в состав DataTable Содержит имя объекта DataTable TabteName String Свойство CaseSensitive Свойство CaseSensitive объекта DataTable определяет, различается ли регистр сим волов при сравнении строк в объекте DataTable. Объект DataSet также предостав ляет свойство CaseSensitive. Значение по умолчанию свойства CaseSensitive объекта DataTable Ч такое же, как значение одноименного свойства объекта DataSet. Если вы зададите значение свойства CaseSensitive объекта DataTable, оно переопределит значение, наследуе мое от родительского объекта DataSet. Значение по умолчанию свойства CaseSensitive объекта DataTable, который не относится к объект^'- DataSet Ч False. Свойства ChildRelations и ParentRelations Свойства ChildRelations и ParentRelations позволяют просматривать объекты Daia Relations с родительскими и дочерними отношениями текущего объекта DataTable. Допустим, вы работаете с иерархией клиентов (customers), заказов (orders; и сведений о заказах (order details) и у вас есть ссылка на объект DataTable с ин формацией о заказах. Тогда в наборе ParentRelations появится объект DataRelation, создающий отношение между объектами DataTable Customers и Orders, а в набо ре ChildRelations Ч объект DataRelation, создающий отношение между объектами DataTable Orders и Order Details. Свойство Columns Свойство Columns позволяет просматривать, добавлять, изменять и удалять объекты DataColumn. Оно возвращает объект DataColumnCollection, содержащий объекты DataColumn из состава DataTable. Обращаться к объектам DataColumn через свойство Columns можно по их имени (свойство ColumnName) или порядковому номеру (свойство Ordinal). Как обыч но, в последнем случае производительность выше. Свойство Constraints Свойство Constraints позволяет просматривать, добавлять, изменять и удалять оп ределенные на объекте DataTable ограничения. Оно возвращает объект Constraints Collection. Обращаться к объектам DataColumn через свойство Columns можно по их имени (свойство ConstraintName) или порядковому номеру в наборе. В последнем слу чае обеспечивается более высокая производительность, 230 Часть III Автономная работа с данными: объект DataSet модели ADO.NET Свойство DataSet Свойство DataSet возвращает одноименный объект, в состав которого входит DataTable. Если последний не относится к какому-либо объекту DataSet, данное свойство возвращает неинициализированный объект. Свойство DataSet доступно только для чтения. Свойство DefaultView Если связать с объектом DataTable элемент управления, последний на самом деле будет связан с объектом DataVieir, возвращаемым свойством DefaultView объекта DataTable. Так, следующий фрагмент кода Ч это фильтр, выводящий в элементе управления DataGrid, связанном с объектом DataTable, только список клиентов из Испаттии (Spain). Независимо от фильтра, объект DataTable по-прежнему содер жит полный список клиентов. Книги, научные публикации