Работа с автономными данными в ADO.NET
ОГЛАВЛЕНИЕ
Самый выжный класс при работе с отсоединенными данными - это DataSet. После того как получены результаты запроса с помощью объекта DataAdapter и сохранены в DataSet' e, соединение между БД и объектом DataSet перестает существовать. Изменения в DataSet не сказываются на БД и наоборот. Класс DataSet включает в себя набор таблиц DataTable и связей между таблицами DataRelation. Класс DataTable включает набор строк DataRow, набор столбцов таблицы DataColumn, и наборы отношений ChildRelations и ParentRelations между столбцами разных таблиц базы данных. Класс DataRow инкапсулирует информацию о строке в таблице и состоянии строки Deleted, Modified, New и Unchanged. Класс Constraint используется для сохранения целостности данных в таблицах.
Преимущества работы с отсоединенными данными:
1. не требуется постоянное соединение с БД, что нужно, например, для web приложений;
2. облегчается создание многоуровневых приложений. Если приложение обращается к БД с помощью объектов уровня DAL, то бизнес объектам на уровне BLL можно передавать DataSet. Обновления в БД также могут передаваться с помощью DataSet;
3. облегчается сортировка, поиск, фильтрация и навигация по данным;
4. облегчается работа с реляционными данными;
5. есть возможность кешировать изменения. Объект DataSet позволяет кешировать изменения и затем с помощью DataAdapter передавать все изменения в БД за 1 раз;
6. тесная интеграция с XML. Содержимое DataSet можно загружать и сохранять в виде XML документов.
Использование объектов DataSet
Ниже приводится пример использования объекта DataSet. Все примеры в статье используют БД Northwind БД MS SQL Server.stringconn ="Provider=SQLSQL;Data Source=(local)\\NetSDK;
InitialCatalog=Northwind;Trusted_Connection=Yes;";
string query = "SELECT CustomerID, CompanyName, ContactName, Phone FROM Customers";
SqlDataAdapter da = new SqlDataAdapter(query, conn);
DataSet ds = new DataSet();
da.Fill(ds, "Customers");
DataTable table = ds.Tables[0];
DataRow row = table.Rows[0];
Console.WriteLine("OrderID: " + row["OrderID"]);
Console.WriteLine("CustomerID: " + row["CustomerID"]);
Изменение содержимого DataTable
Для добавления новой строки в таблицу существует метод NewRow(), который создает новый объект DataRow, но не добавляет запись в таблицу. Полем новой записи задаются значения по умолчанию или Null, если значение по умолчанию не задано.DataRow row = ds.Tables["Customers"].NewRow();
row["CustomerID"] = "VASYA";
row["Company"] = "Компания";
row["ContactName"] = "Вася Пупкин";
row["Phone"] = "11-22-33";
ds.Tables["Customers"].Rows.Add(row);
object[]aValues = {"VASYA", "Компания", "Вася Пупкин", "11-22-33"};
ds.Tables["Customers"].LoadDataRow(aValues, false);
DataRow row = ds.Tables["Customers"][0];
row["ContactName"] = "Вася Пупкин";
object[] items = {"VASYA", "Компания", "Вася Пупкин", "11-22-33"};
DataRow row = ds.Tables["Customers"][0];
Row.ItemArray = items;
Использование столбцов с автоинкрементном
Для поддержки столбцов с автоматическим увеличением значения у DataColumn есть свойства AutoIncrement, AutoIncrementSeed и AutoIncrementStep . Для того, чтобы создать столбец DataColumn с автоинкрементом нужно установить свойство AutoIncrement в true. Значение счетчика будет начинаться с AutoIncrementSeed (если это не нарушает корректность данных в таблице) и увеличиваться на значение AutoIncrementStep. Для столбцов с автоинкрементном рекомендуется устанавливать свойство ReadOnly в true. Ниже приведен пример вставки в таблицу столбца с автоинкрементомDataColumn column = table.Columns.Add("CustomerID", typeof(Int32));
column.AutoIncrement = true;
column.AutoIncrementSeed = 1;
column.AutoIncrementStep = 1;
Если вы планируете сносить изменения в БД методом DataAdapter.Update(), то рекомендуется задать свойствам AutoIncrementSeed и AutoIncrementStep значения -1. Это гарантирует, что ADO.NET будет генерировать значения метки, которых нет в БД. При вызове метода Update в БД внесутся уже уникальные значения.
Работа с DataSet в среде Visual Studio .NET
Создавать объекты DataSet можно как программно, так и с помощью среды Visual Studio. Для второго случая предназначена вкладка Data в панели инструментов. Для начала нужно перетащить на web или win-форму объект SqlDataAdapter.Добавленный объект DataSet отобразился в панели компонентов. Мы добавили т.н. DataSet со строгим контролем типов (типизированный DataSet ), о котором будем говорить позже. Структуру данных можно посмотреть в созданном файле с расширением xsd.
Работа с реляционными данными
Работа с реляционными данными осуществляется с помощью объектов класса DataRelation, связующие колонки DataColumn двух таблиц DataTable. Создать такой объект можно следующим образом:ds.Relationships.Add(new DataRelation(
"CustomersOrders", ds.Tables["Customers"].Columns["CustomerID"],
ds.Tables["Orders"].Columns["CustomerID"]);
DataTable tblParent = ds.Tables["ParentTable"];
DataColumn[] colsParent = new DataColumn[] {tblParent.Columns["ParentColumn1"], tblParent.Columns["ParentColumn2"]};
DataTable tblChild = ds.Tables["ChildTable"];
DataColumn[] colsChild = new DataColumn[] {tblChild.Columns["ChildColumn1"], tblChild.Columns["ChildColumn2"]};
ds.Relationships.Add(new DataRelation("ParentChild", colsParent, colsChild));
Работа со связанными данными
Основным применением объектов DataRelation является поиск связанных данных. Однако сам объект DataRelation не предоставляет такой функциональности, она реализуется методами класса DataRow: GetChildRow(), GetParentRow() и GetParentRows().Для поиска дочерних записей используется метод GetChildRow() соответсвующего объекта DataRow. Ему передается ему имя объекта DataRelation, определяющего отношение. Этот метод возвращает дочерние записи в виде массива объектов DataRow :
DataRow row = ds.Tables["Customers"].Rows[0];
foreach (DataRow rowOrder in row.GetChildRows("CustomersOrders"))
Console.WriteLine(rowOrder["OrderID"] + rowOrder["OrderDate"] + "\n");
DataRow rowOrder = ds.Tables["Orders"].Rows[0];
Console.WriteLine("\t" + rowOrder.GetParentRow("CustomersOrders")["ContactName"] + "\n");
В случае отношения <один ко многим> найти все родительские записи конкретного объекта DataRow можно с методом GetParentRows(), также принимающего название отношения и возвращающего массив родительских объектов.
Добавление объекта DataRelation в Visual Studio .NET
Для добавления отношения DataRelation в DataSet со строгим контролем типов нужно перейти к структуре данных, щелкнув по файлу с расширением xsd, определяющего структуру DataSet. Щелкнув на родительский DataTable правой кнопкой нужно в контекстном меню выбрать Add / New Relation.В диалоговом окне Edit Relation задаются родительские и дочерние объекты.
Поиск записей
При запросах к БД часто требуется найти запись по ее первичному ключу. Для поиска записей в DataTable существует метод DataRowCollection.Find ().table.PrimaryKey = new DataColumn[] {tbl.Columns["CustomerID"]};
DataRow row = tbl.Rows.Find("VASYA");
if (row == null)
Console.WriteLine("Запись не найдена");
else
Console.WriteLine(row["CompanyName"]);
Для выполнения поиска по произвольному SQL запросу у DataTable есть метод Find(). Например, для получения имен всех покупателей, находящихся в Уфе, можно воспользоваться таким кодом
DataRow[] rows = table.Select("City = 'Уфа'");
foreach (DataRow row in rows)
Console.WriteLine(row["CompanyName"] + "\n");
DataRow[] rows = table.Select("City = 'Уфа'", "CompanyName ASC");
Работа с объектами DataSet со строгим контролем типов
Ранее мы уже создавали объекты DataSet со строгим контролем типов. Рассмотрим, какие новые функции появились у типизированного DataSet.Добавление записи
Все классы, соответствующие таблицам DataTable в DataSet, позволяют добавлять новые записи двумя способами. Метод New[Имя_таблицы]Row() возвращает новую строку в таблице:DataSetTest ds = new DataSetTest();
DataSetTest.CustomersDataTable tblCustomers = ds.Customers;
DataSetTest.CustomersRow rowCustomer = tblCustomers.NewCustomersRow();
rowCustomer.CustomerID = "VASYA";
rowCustomer.CompanyName = "Компания";
rowCustomer.ContactName = "Вася Пупкин";
rowCustomer.Phone = "11-22-33";
tblCustomers.AddCustomersRow(rowCustomer);
DataSetTest ds = new DataSetTest();
DataSetTest.CustomersDataTable.AddCustomersRow("VASYA", "Компания", "Вася Пупкин", "11-22-33");
Поиск записи
Поиск записи в таблице тоже претерпел изменения. Например, для поиска в таблице Order Details по составному ключу появился метод следующий методDataSetTest.Order_DetailRow = tblDetails.FindByOrderIDProductID(112233, 456);
Также типизированные DataSet поддерживают русские буквы в названиях таблиц и полей таблиц.ds.Статистика.AddСтатистикаRow(15.9, "компания", true );
Передача обновлений в БД
Для передачи изменений в БД используется объекты DataAdapter. Для создания логики обновления БД можно использовать один из трех вариантов:1. вручную сконфигурировать DataAdapter в период разработки;
2. воспользоваться объектом CommandBulder в период выполнения;
3. использовать в период разработки DataAdapter Configuration Wizard.
Ручное конфигурирование DataAdapter
DataAdapter имеет 3 свойства для передачи изменений в БД: InsertCommand, UpdateCommand и DeleteCommand. Значения этих свойства должны быть заданы до вызова метода DataAdapter.Update() с учетом того, какие изменения были внесены в DataSet. Когда метод Update() добавляет, обновляет или удаляет строки в таблице, он вызывает соответствующую команду.Если в ходе ваших действий вы только добавляли строки в таблицу, то и задать вы должны только значение InsertCommand. Ниже приведен код для такой ситуации:
string conn = "Provider = SQLSQL; Data Source = (local)\\NetSDK; Initial Catalog= Northwind; Trusted_Connection=Yes;";
string query = "SELECT CustomerID, CompanyName, ContactName, Phone FROM Customers"; SqlDataAdapter da = new SqlDataAdapter(query, conn); DataSet table = new DataSet(); da.Fill(table, "Customers");
// добавляем новые строки в таблицу
...
// создаем команду для вставки новых записей
query = "INSERT INTO Customers (CustomerID, CompanyName, ContactName, Phone) VALUES (?, ?, ?, ?)";
SqlCommand cmd = new SqlCommand(query, conn);
SqlParameterCollection pc = cmd.Parameters; pc.Add("CustomerID", SqlType.Integer, 0, "CustomerID");
pc.Add("CompanyName", SqlType.String, 0, "CompanyName");
pc.Add("ContactName", SqlType.String, 0, "ContactName");
pc.Add("Phone", SqlType.String, 0, "Phone");
da.InsertCommand = cmd;
da.Update(table);
OleDbCommand cmd = new OleDbCommand("spInsertCustomer", conn);
cmd.CommandType = CommandType.StoredProcedure;
OleDbParameterCollection pc = cmd.Parameters;
pc.Add("CustomerID", SqlType.Integer, 0, "CustomerID");
pc.Add("CompanyName", SqlType.String, 0, "CompanyName");
pc.Add("ContactName", SqlType.String, 0, "ContactName");
pc.Add("Phone", SqlType.String, 0, "Phone");
Использование CommandBuilder
CommandBuilder генерирует SQL запросы обращаясь к БД для получения метаданных о таблицах. Для использования CommandBuilder необходимо выполнение следующих условий: 1. запрос возвращает данные только из одной таблицы;2. в таблице определен первичный ключ;
3. первичный ключ есть в результатах запроса.
Ниже приведен пример использования CommandBuilder.
string con ="Provider=SQLOLEDB;Data Source=(local)\\NetSDK;InitialCatalog=Northwind;
Trusted_Connection=Yes;";
string query = "SELECT OrderID, ProductID, Quantity, UnitPrice FROM [Order Details]";
OleDbDataAdapter da = new OleDbDataAdapter(strSQL, strConn);
OleDbCommandBuilder cb = new OleDbCommandBuilder(da);
da.InsertCommand = cb.GetInsertCommand();
da.Update(tbl);
Использование мастера Data Adapter Configuration Wizard
Одно из предназначений мастера - создание логики обновления базы данных. На четвертом шаге мастера по команде Advanced Options выводится диалог, в котором можно указать создавать или нет команды для вставки, обновления и удаления записей в БД.Кондратьев Денис