Тема 2. «Программирование в средах современных информационных систем» Цель Рассмотреть новую стратегию доступа к данным в современных информационных системах. Задачи 1. Познакомиться с принципами новой стратегии доступа к данным. 2. Изучить элементы теории модульного программирования. 3. Рассмотреть объекты доступа к данным, реализованным в концепции ADO.NET. Оглавление Передача данных в современных информационных системах Объект DataSet: свойства и методы Объект DataTable Объект DataRow Объект DataColumn Объект DataRelation Выводы Вопросы для самопроверки Литература Передача данных в современных информационных системах Основная идея передачи данных в современных информационных системах состоит в том, что в сегодняшнем мире, для которого характерен высокий уровень информационной интеграции, приложение клиента может использовать данные из множества источников. В этой ситуации, получив требуемую информацию через сеть за достаточно короткий промежуток времени, дальнейшую обработку данных требуется выполнять локально, без постоянной поддержки соединения с хранилищами данных (рис. 2.1). Уровень пользовательского интерфейса XML Промежуточный уровень Уровень данных DataReader DataAdapter Хранилище данных Command DataSet Другие источники данных Connection Рис. 2.1. Современная технология отсоединяемого доступа к данным Концепция ADO.NET предоставляет средства доступа к данным: через объекты DataReader и DataSet. Объект DataReader предназначен для последовательного считывания информации, и поэтому используется для быстрого и эффективного доступа к потоковым данным при непосредственном (не разрывном) соединении с источником данных. Для реализации отсоединенного доступа к данным с последующей их автономной обработкой в технологии ADO.NET применяется объект DataSet. Объект DataSet можно считать главным объектом в ADO.NET, представляющим собой копию данных, размещаемых в оперативной памяти клиентского компьютера. Данные в объекте DataSet могут быть организованы в реляционные таблицы, связанные (или не связанные) между собой. По сути, объект DataSet является временной базой данных, хранимой в оперативной памяти, которая не связана активными подключениями с источниками данных, из которых была извлечена информация. Чтобы разобраться, как работает DataSet, взгляните на рис. 2.2. DataSet DataTable DataColumn DataTable DataColumn DataColumn DataRow DataColumn DataRow DataRelation Рис. 2.2. Структура объекта DataSet Из рис. 2.2 видно, что объект DataSet может включать один или несколько объектов DataTable, в которых содержится информация, извлеченная из хранилищ данных. Каждый объект DataTable внутри DataSet в свою очередь включает объекты DataColumn, определяющие столбцы, данные из которых хранятся в объекте DataSet. Точно также внутри объекта DataSet может находиться произвольное количество объектов DataRow, в которых размещаются сами строки (записи) данных. Объекты DataRelation используются для описания отношений между таблицами в DataSet. Объект DataSet использует для передачи данных между платформами и различными архитектурами формат XML. Формат XML позволяет описывать иерархию данных в текстовом формате, что является идеальным решением для коммуникации между системами путем организации стандартного способа общения, понятного для каждой системы. Стандарты XML разрабатываются консорциумом W3C (независимой организацией, специально созданной для разработки и утверждения стандартов на коммуникационные протоколы). Для того чтобы системы могли обмениваться данными друг с другом, информация должна быть представлена в некотором стандартизованном формате (отформатирована), чтобы в дальнейшем можно было выполнить ее грамматический разбор. Предположим, вы передаете системе фрагмент данных, который выглядит следующим образом: Табуреткин Василий Вы, конечно, догадались, что речь идет об имени некоторого человека, но с какой целью эта информация была передана? Кто этот человек, и зачем нужна информация о нем? Рассмотрим другой вариант представления передаваемых данных: <Order> <CustomerName>Табуреткин Василий</CustomerName> </Order> Теперь у нас появилась дополнительная смысловая информация. К данным были добавлены теги разметки, которые говорят о том, что некто Табуреткин Василий поместил какой-то заказ и теперь является клиентом. Вероятно, эта и другая более детальная информация о заказе отсылается поставщику, который должен выполнять заказ. Подумайте о том, какие преимущества дает использование стандартного способа разметки текстовых данных, которые могут быть впоследствии переданы на различные платформы? В это состоит вся сила XML. Объект DataSet: свойства и методы Использование объекта DataSet позволяет работать с автономными наборами данных без активного подключения. Объект DataSet сам по себе не представляет ничего примечательного. Фактически он является эквивалентом базы данных на SQL Server. И точно так же, как в обычную базу данных, в него могут быть помещены таблицы, столбцы и строки, в нем могут создаваться отношения и ограничения. Все перечисленные элементы формируют схему или структуру объекта DataSet, поэтому мы по очереди рассмотрим каждый элемент. Но перед этим разберем некоторые методы объекта DataSet более подробно (могут быть проблемы при их вызове). В приведенном ниже фрагменте программного кода создается новый объект DataSet. Затем используется метод SqlDataAdapter.Fill для помещения в пока еще пустой объект DataSet новой таблицы, содержащей строки и столбцы, полученные в результате выполнения запроса: Imports System.Data.SqlClient Dim strConnection As String="server=Persist Security Info=False; Integrated Security=SSPI; database=HumanResources; server=abrzh" Dim cnn As New SqlConnection(strConnection) Dim ds As New DataSet() Dim ad As New SqlDataAdapter(“SELECT * FROM Orders”, cnn) da.Fill(ds) Метод AcceptChanges фиксирует все изменения, внесенные в объект DataSet, однако не позволяет зафиксировать их в источнике данных. Обращаться с этим методом надо осторожнее, так как вызов этого метода изменяет свойство RowState на Unchanged, что не позволит внести изменения в источник данных (пропадает возможность идентифицировать: какие данные должны быть перенесены в источник). Если вы хотите сохранить вносимые изменения в источнике данных, нельзя вызывать метод AcceptChanges напрямую. Лучше воспользоваться методом Update у объекта DataAdapter, в результате выполнения которого изменения вначале будут зафиксированы в источнике данных, а уже потом сохранены в объекте DataSet. Предположим, мы модифицировали данные в первой строке таблицы: ds.Tables(0).Rows(0).Item(“Name”)=”Сидоркин” если мы теперь вызовем метод AcceptChanges для записи изменений в DataSet (ds.AcceptChanges), то не сможем эти изменения внести в источник данных. Метод GetChanges позволяет создать новый объект DataSet, в который будут помещены строки таблиц, модифицированные с момента предыдущего вызова метода AcceptChanges. Dim chandesDs As DataSet=ds.GetChanges Метод RejectChanges во многом подобен AcceptChanges в том смысле, что выполняемые им действия не оказывают влияния на сами источники данных. Данный метод вызывается в случае необходимости отмены изменений, внесенных в объект DataSet. В результате его применения все данные будут приведены в состояние, которое они имели сразу после извлечения либо после последнего вызова метода AcceptChanges. Метод Reset используется для удаления всех существующих объектов и коллекций из объекта DataSet. Это простейший способ очистки объекта DataSet в случае, когда вы собираетесь использовать его для выполнения новой задачи. Объект DataTable Фактически, объект DataTable представляет собой набор записей, возвращаемых запросом из SqlDataAdapter. Если у вас имеется DataTable несколько наборов записей, другими словами вы используете более одного запроса SELECT в свойстве SelectCommand, DataColumn DataColumn тогда вам будет возвращена целая DataRow коллекция объектов DataTable. Объект DataTable внутри объекта DataSet может быть создан несколькими различными способами. Первый способ подразумевает вызов метода Add свойства DataSet.Tables: Dim ds As New DataSet() ds.Tables.Add(“MyTable”) Второй вариант создания: вначале описать объект DataTable, а уже затем добавить его в объект DataSet: Dim ds As New DataSet() Dim tbl As New DataTable(“MyTable”) ds.Tables.Add(tbl) И, наконец, третий способ: создать объект DataTable при заполнении DataSet результатами запроса к SQL Server: Imports System.Data.SqlClient Dim strConnection As String="server=Persist Security Info=False; Integrated Security=SSPI; database=HumanResources; server=abrzh" Dim cnn As New SqlConnection(strConnection) Dim ds As New DataSet() Dim td As DataTable Dim ad As New SqlDataAdapter(“SELECT * FROM Orders”, cnn) ad.TableMappings.Add(“Table”, “MyTable”) da.Fill(ds) td=ds.Tables(“MyTable”) Метод TableMappings.Add объекта SqlDataAdapter позволяет назначать имена таблицам, которые будут созданы в объекте DataSet при его заполнении результатами выполнения команды SQL. Для вставки строк в существующий объект DataTable имеются методы: BeginLoadData/ EndLoadData/ LoadDataRow, NewRow/ ImportRow. Метод BeginLoadData обеспечивает самый быстрый способ добавления строк в таблицу. На время загрузки отключается поддержка индексов и проверка ограничений. При вызове метода EndLoadData выполняется перестройка индекса и активизация ограничений. При этом все добавленные в DataTable записи проверяются на соответствие ограничениям. Метод LoadDataRow может использоваться для добавления отдельных строк из любого массива данных. Выполняется проверка на уникальность значения первичного ключа, в соответствии с проверкой запись либо редактируется, (ключ существует), либо добавляется вновь (ключ новый). Dim MyValues(1) As Object MyValues(0)=”Табуреткин” MyValues(1)=”Василий” ds.Tables(“MyTable”).BeginLoadData() ds.Tables(“MyTable”).LoadDataRow(MyValues,True) ds.Tables(“MyTable”).EndLoadData() Методы NewRow и ImportRow выполняют создание новой строки в существующий объект DataTable. Различия между результатами применения методов заключаются в состоянии и значении свойств, получаемых после добавления строки: метод NewRow выполняет создание новой строки и устанавливает ее статус равным Added, а при использовании метода ImportRow строка будет иметь статус Unchanged. Метод Select используется для возвращения массива объектов DataRow, соответствующих определенному критерию. В качестве такого критерия может выступать некое условие для значения столбца либо значение статуса строки, например, Added. В нашем примере метод Select используется для извлечения массива объектов DataRow, для которых значение в столбце Name начинается с буквы “Т”: Dim drs As DataRow() drs=ds.Tables(“Mytable”).Select(“Name like ‘Т%’”) Объект DataRow Данные, полученные в результате выполнения запроса, хранятся в объекте DataRow. Поскольку связь с источником данных не поддерживается, то для обновления информации требуется использовать свойства и методы объекта DataRow. Рассмотрим некоторые из них. Свойство RowState используется при модификации данных в таблице, будь-то удаление, изменение либо добавление строк. Каждая строка DataRow имеет определенное значение свойства RowState, благодаря этому значению можно определить, какие операции были произведены над данной строкой. Начальное значение свойства RowState для любой строки коллекции – Unchanged. После изменения данных статус строки становится равным Modified. При удалении строки свойство RowState получает значение Deleted. Свойство Item позволяет извлечь данные из определенного столбца либо поместить данные в определенный столбец. Свойство ItemArray позволяет обрабатывать массив значений при заполнении столбцов таблицы. Методы BeginEdit и EndEdit используются в паре для начала и завершения редактирования строки. Dim rw As DataRow rw=ds.Tables(“Mytable”).Row(0) rw.BeginEdit(0 If rw.HasVersion(DataRowVersion.Current) Then rw.Item(“Name”)= ”Сидоркин” rw.EndEdir(0 rw.AcceptChanges() Объект DataColumn Объект DataColumn используется для определения структуры объекта DataTable. Каждый объект DataRow, добавляемый в DataTable, должен содержать значение для всех столбцов текущей таблицы. Важно понимать, что значения столбцов не могут быть извлечены с помощью коллекции DataColumn объекта DataTable. Коллекция объектов DataColumn используется исключительно для формирования структуры, а доступ к отдельным значениям столбцов можно получить при помощи свойства DataRow.Item. Рассмотрим наиболее интересные свойства и методы объекта DataColumn. Свойство AllowDBNull определяет допустимость Null-значений для заданного столбца. Свойство AutoIncrement говорит о том, что рассматриваемый столбец содержит значение, автоматически генерируемое для каждой новой строки с заданным приращением. Это свойство является эквивалентом столбца Identity в SQL Server. Свойство AutoIncrementSeed задает начальное значение для столбца так называемых identity значений. Свойство AutoIncrementStep определяет шаг приращения для столбца-аналога identity-значений. Свойства ColumnName, DataType, DefaultValue задают соответственно имя столбца, тип данных столбца, значение по умолчанию для столбца. Свойство DataType может принимать следующие значения: Boolean, Byte, Char, DateTime, Decimal, Double, Int16, Int32, Int64, SByte, Single, String, TimeSpan – для оценки разности между двумя значениями типа DateTime, UInt16, UInt32, UInt64 – для беззнаковых целых чисел. Dim ds As New DataSet() Dim rw As DataRow Dim col As DataColumn Ds.Tables.Add(“Mytable”) col=ds.Tables(“Mytable”).Columns.Add(“IDCol”, Type.GetType(“System.Int32”)) col.AutoIncrement=True col.AutoIncrementSeed=1 col.AutoIncrementStep=10 Для создания вычисляемых столбцов, либо столбцов, получаемых путем агрегации данных, используется свойство Expression. Рассмотрим пример, в котором для получения значения вычисляемого столбца используются итоговые (агрегатные) функции. В следующем программном коде определяется максимальная цена в столбце price для таблицы Mytable, из которой затем вычитается цена в текущей строке для подсчета разницы в столбце MaxPriceDiff: Imports System.Data, System.Data.SqlClient Dim strConnection As String="server=Persist Security Info=False; Integrated Security=SSPI; database=HumanResources; server=abrzh" Dim cnn As New SqlConnection(strConnection) Dim ds As New DataSet() Dim ad As New SqlDataAdapter(“SELECT * FROM Orders”, cnn) ad.TableMappings.Add(“Table”, “MyTable”) da.Fill(ds) Dim col As New DataColumn() col.DataType=Type.GetType(“System.Decimal”) col.ColumnName=”MaxPriceDiff” col.Expression=(“MAX(price)-price”) ds.Tables(“Mytable”).Columns.Add(col) Объект DataRelation Мы уже не раз говорили, что объект DataSet может рассматриваться как некая копия DataTable DataTable базы данных, размещенная в памяти. Однако существует немаловажное отличие. Тогда как объекты DataTable отражают DataRelation структуру соответствующих таблиц базы данных, отношения между DataTable не могут быть получены (при заполнении DataSet с помощью метода Fill) неявным образом на основе имеющихся сведений об ограничениях ссылочной целостности, существующих в базе данных. В дальнейшем вам необходимо будет воспользоваться объектом DataRelation для создания отношений между объектами DataTable объекта DataSet, чтобы обеспечить, таким образом, защиту целостности данных, размещенных в DataSet, а также выполнения обновлений в дочерних объектах DataTable. Объект DataRelation может быть создан путем вызова метода Relations.Add объекта DataSet. Чтобы воспользоваться этим методом, необходимо задать имя отношения, а также указать родительские и дочерние столбцы отношения в виде имен двух объектов DataColumn, например: Dim ad1 As New SqlDataAdapter(“SELECT * FROM Employees”, cnn) ad1.TableMappings.Add(“Table”, “Employees”) Dim ad2 As New SqlDataAdapter(“SELECT * FROM Orders”, cnn) ad2.TableMappings.Add(“Table”, “Orders”) Dim ds As New DataSet() da1.Fill(ds) da2.Fill(ds) Dim ParentCol As DataColumn= ds.Tables(“Employees”).Columns(“EmployeeID”) Dim ChildCol As DataColumn= ds.Tables(“Orders”).Columns(“EmployeeID”) Ds.Relations.Add(“FK_Departments”, ParentCol, ChildCol) Как видите, мы создали два новых объекта DataColumn: один для столбца, выступающего в роли родительской части, а другой – для столбца ключа дочерней части отношения. Если бы мы захотели создать отношений из нескольких столбцов, нам понадобилось бы DataSet создать массив объектов DataColumn и перечислить их все при создании отношения с помощью объекта DataRelation. Чтобы заставить DataRelation поддерживать целостность данных между объектами DataTable, необходимо создать ограничение, обеспечивающее реализацию отношений между родительскими и дочерними объектами DataColumn, а значение свойства EnforceConstraints объекта dataset установить равным True. Данное ограничение реализуется в виде ограничения уникальности UniqueConstraint для родительских таблиц и в качестве ограничения внешнего ключа для дочерних таблиц. Указанные ограничения создаются автоматически при определении отношения либо могут быть созданы независимо от объекта DataRelation для поддержки других (не ссылочных) ограничений целостности данных, например, ограничений уникальности или ключей-кандидатов. Ограничение уникальности UniqueConstraint означает, что для каждой строки значение в данном столбце или столбцах должно являться уникальным (неповторяющимся). Ограничение имеет набор свойств, которые можно изменять. Ограничение внешнего ключа ForeignKeyConstraint необходимо в случае, если значение в данном столбце одной таблицы должно соответствовать одному их значений столбца другой таблицы. Реализация такого типа ограничений позволяет защитить вас от ошибок в логике приложений, когда в одну таблицу добавляются данные, зависимые от несуществующей строки другой таблицы. Объект ForeignKeyConstraint также имеет набор свойств, предназначенных для изменения. Аналогично ограничению уникальности, ограничения внешнего ключа могут создаваться автоматически при определении отношения DataRelation между объектами DataTable. Целостность данных вполне может поддерживаться и без создания объекта DataRelation. Но в этом случае вы не сможете осуществлять навигацию между таблицами при помощи коллекции отношений объекта DataSet либо свойств ChildRelations и ParentRelations отдельных таблиц. Выводы На сегодняшний день актуальными являются технологии доступа к данным, позволяющие выполнять распределенную обработку информации в автономном (отключенном) от источника режиме. Концепция ADO.NET предоставляет разработчику средства доступа к данным и средства хранения данных в автономном режиме. Мы рассмотрели функциональность модели ADO.NET в отключенном от источника данных режиме работы. Изучили объект модели DataSet и связанные с ним объекты DataTable, DataRow, DataColumn, DataRelation, Constraint. Вопросы для самопроверки 1. В чем заключается современная идея передачи данных в распределенные приложения от удаленных источников данных? 2. Какие возможности предоставляет концепция .NET для реализации современной идеи доступа к удаленным данным? 3. Какова структура объекта DataSet, реализующего автономное хранение данных в технологии ADO.NET? 4. Какие преимущества дает использование стандартного способа разметки текстовых данных, которые могут быть впоследствии переданы на различные платформы? 5. Как поместить данные из источника в объект приложения DataSet? 6. Какие проблемы могут возникать при использовании метода AcceptChanges объекта DataSet? 7. Какие способы создания объектов DataTable вы можете перечислить? 8. Каким образом можно найти нужную информацию в автономном хранилище данных? 9. Почему свойство RowState можно считать наиболее важным для объекта DataRow? 10. Какова роль объекта DataColumn в структуре объекта DataSet? 11. Наблюдается ли полное соответствие типов данных платформы .NET и значений свойства DataType объекта DataColumn? 12. Можно ли в структуру объекта DataColumn добавлять вычисляемые столбцы? Каким образом? 13. Для чего используется объект DataRelation? Можно ли обойтись без него? Литература 1. Т. Бэйн, Д. Госнелл, Дж. Уолш. Visual Basic .NET и SQL Server 2000: эффективный уровень данных. / Пер. с англ.; Под ред. С.М. Молявко. – М.: БИНОМ. Лаборатория знаний, 2006. – 604 с., ил. 2. Объектно-ориентированное программирование в Visual Basic.NET. Библиотека программиста / Д. Кларк. — СПб.: Питер, 2003. — 352 с.: ил. 3. Эпплман Д. Переход на VB.NET: стратегии, концепции, код. — СПб.: Питер, 2002. — 464 с.: ил. 4. Троелсен Э. C# и платформа .NET. Библиотека программиста — СПб.: Питер, 2006 — 796 с.: ил.