Высокоуровневые методы информатики и программирования Лекция 28 Отсоединенный режим работы ADO.Net Присоединенный режим работы с БД Наша программа Читаем в цикле используя операцию [ ] DataReader ExecuteScalar() - получаем одно значение ExecuteReader() Command Command • SQL Select • хранимая процедура • SQL Insert, Update, Delete • хранимая процедура ExecuteNonQuery() Connection БД : MS SQL Server, Oracle, MS Access, Foxpro, DBase и т.п. Отсоединенный режим работы с БД Использование классов ADO.NET ExecuteReader DataReader Command Connection Read Update, Insert, Delete Data Base DataAdapter Provider ExecuteXx x Client Program Fill Update DataSet Компьютер пользователя SQL Server организации Класс DataSet • Объекты класса DataSet позволяют создать базу данных в оперативной памяти. – Данные извлекаются из базы данных и сохраняются в объекте DataSet. – Объект DataSet содержит набор данных отсоединенных от БД. – Затем программа может изменять эти данные • Обновления сделанные в DataSet должны быть занесены обратно в БД для того, чтобы сохранить их постоянно. • Класс DataSet является одним для всех провайдеров по работе с БД. • Объекты класса DataSet включают объекты DataTable (таблицы данных) и DataRelation (связи между таблицами). • Для заполнения объекта DataSet используется класс DataAdapter из провайдера. Наша программа DataTable Метод Fill() DataTable DataTable DataSet Метод Update() DataAdapter DataAdapter Connection DataAdapter Command Command Command Command ExecuteNonQuery() БД : MS SQL Server, Oracle, MS Access, Foxpro, DBase и т.п. Конструкторы класса DataSet • DataSet ds = new DataSet(); • DataSet ds = new DataSet("MyDataSet"); Структура класса DataSet DataSet .Tables[...] DataTable DataTable schema .Columns[...] .Columns[..] DataColumn DataColumn .Rows[...] .Rows[..] DataRow DataRow .DefaultView ... .Relations[...] ... DataView DataRelation DataRelation данные Свойства класса DataSet Основные: • Tables – коллекция таблиц DataTable • Relations – коллекция отношений DataRelation Дополнительные свойства: • ExtendedProperties – коллекция дополнительных свойств (имя, значение) • CaseSensitive – указывает, будет ли при сравнении строк в DataTable объектах учитываться регистр (большие и маленькие символы). • DataSetName – дружественное имя данного DataSet. Обычно задается в конструкторе класса. • EnforceConstraints – получает или задает значение указывающее на то, будут ли учитываться ограничивающие правила (constraint rules) при попытке выполнить любую операцию обновления. • HasErrors – получить значение указывающее на наличие ошибок в любой строке любой таблицы DataTables данного DataSet. • RemotingFormat – позволяет определить то, как будет сериализоваться содержание DataSet (binary или XML). Основные методы DataSet • • • • • • • • • • • Clear() – полное удаление всех записей во всех DataTable таблицах DataSet. Merge() – слияние данного DataSet с указанным DataSet. GetChildRelations() – получить коллекцию дочерних отношений указанной таблицы. GetParentRelations() - получить коллекцию родительских отношений указанной таблицы. HasChanges() – определить, изменился ли DataSet, включая новые, удаленные или измененные записи (rows). AcceptChanges() – принятие (commits) всех изменений сделанных в этом DataSet с момента загрузки в него данных или последнего вызова метода AcceptChanges(). RejectChanges() – отменить все изменения, сделанные в данном DataSet с момента его создания или последнего вызова метода AcceptChanges(). ReadXml() – чтение XML данных из потока (файла, оперативной памяти, или сети) в DataSet. ReadXmlSchema() – чтение структуры WriteXml() – записать содержание DataSet в поток. WriteXmlSchema() – записать структуру DataSet в поток. Создание и заполнение таблицы с помощью DataAdapter SqlConnection conn = new SqlConnection(conString); SqlDataAdapter da = new SqlDataAdapter( "SELECT pub_id, pub_name FROM Publishers", conn); DataSet pubsDataSet = new DataSet("Pubs"); da.Fill(pubsDataSet, "publishers"); Использование классов ADO.NET ExecuteReader DataReader Command Connection Data Base Read Select Update, Insert, Delete DataAdapter Provider ExecuteXx x Client Program Fill Update DataSet Компьютер пользователя SQL Server организации Класс DataAdapter • Для организации взаимодействия DataSet с БД. • Организует взаимодействие одной таблицы DataSet с базой данных • Свойства – – – – SelectCommand содержит SQL SELECT оператор InsertCommand содержат SQL INSERT UpdateCommand содержат SQL UPDATE DeleteCommand содержат SQL DELETE операторы • Методы – Fill(ds) использует соединение для заполнения DataSet данными • Первый параметр – объект DataSet • Второй (необязательный) – название создаваемой таблицы – Update() посылает исправленные в DataSet данные в БД • Параметр – объект DataSet Конструкторы класса DataAdapter • Конструктор класса DataAdapter является перегруженным: – без параметров SqlDataAdapter da = new SqlDataAdapter(); – с одним параметром – объектом Command: SqlDataAdapter da = new SqlDataAdapter(cmd); – с двумя параметрами – SQL оператором и объектом Connection: SqlDataAdapter da = new SqlDataAdapter(sql, conn); – с двумя параметрами – SQL оператором и строкой соединения: SqlDataAdapter da = new SqlDataAdapter(sql, connString); • Параметры: – – – – conn – объект Connection; connString – строка соединения. sql – оператор языка SQL; cmd – объект Command; Методы Fill() и Update() • Метод int Fill(): – int Fill (DataSet ds) – в DataSet создается и заполняется данными таблица БД (к данной таблице можно обращаться только по номеру); – int Fill (DataTable dt) – заносит в таблицу схему и данные; – int Fill (DataSet ds, string srcTable) – в DataSet создается и заполняется данными таблица с именем srcTable (к данной таблице можно обращаться только по имени); • Метод int Update(): – int Update(DataSet ds) – в БД сохраняются изменения всех таблиц DataSet; – int Update(DataSet ds, string srcTable) – в БД сохраняются изменения таблицs с данным именем; – int Update(DataRow[] drs) – в БД сохраняются изменения сделанные в записях из массива. Последовательность работы с DataAdapter • Создать объект Connection. • Создать экземпляр класса и задать объект Command с SQL командой Select. • Создать объект класса CommandBuilder и построить в DataAdapter команды Insert, Update и Delete. • Вызвать для DataAdapter метод Fill(), в который передать объект DataSet. • Выполнять работу с DataSet • После окончания работы вызвать для DataAdapter метод Update(), в котором передается использованный объект DataSet. Пример SqlDataAdapter ad = new SqlDbDataAdapter (“select ProductID, ProductName, UnitPrice from Products”, conn); SqlCommandBuilder bld = new SqlCommandBuilder (ad); DataSet ds = new DataSet(); ad.Fill (ds) ; // можно задать и другое имя таблиц - , “Products”); DataTable tbl = ds.Tables["Products"]; for (int i = 0; i < tbl.Rows.Count; i++) { Console.WriteLine("id={0};name={1};цена={2}", tbl.Rows[i][0], tbl.Rows[i][1], tbl.Rows[i][2]); } Класс CommandBuilder • Данный класс используется для упрощения создания набора SQL команд. • Может применяться, когда используется оператор Select для одной таблицы БД. • По заданной в DataAdapter SQL команде select создаются соответствующие ей команды – InsertCommand содержат SQL INSERT – UpdateCommand содержат SQL UPDATE – DeleteCommand содержат SQL DELETE операторы • Эти команды требуются для выполнения метода Update (перенесение всех изменений из DataSet в БД). • Для создания новых команд нужно создать объект класса CommandBuilder и передать ему в качестве параметра объект DataAdapter. – Например: SqlCommandBuilder св = new SqlCommandBuilder(da); Пример использования объекта DataAdapter // создаем DataAdapter SqlDataAdapter da = new OleDbDataAdapter (“select ProductID, ProductName, UnitPrice from Products”, conn); // автоматически строим команды Insert, Update,Delete SqlCommandBuilder cb = new SqlCommandBuilder(oDA); DataSet ds = new DataSet(); da.Fill(ds); decimal newPrice = (decimal) tbl.Rows[0][“UnitPrice "] + 1.0M; tbl.Rows[0]["UnitPrice"] = newPrice; da.Update(ds, “Products”); Класс DataTable • Полное имя класса System.Data.DataTable. • Концептуально аналогичен таблице в БД. • Содержит две коллекции: – строк Rows; – столбцов Columns – объекты с описаниями полей записей. • Может использоваться, для представления самостоятельной таблицы или таблицы в DataSet. • Для получения значений колонок таблицы можно использовать индексаторы tbl[i][j], где – i – номер строки; – j – номер колонки (можно задать и название колонки). • Например: DataTable tbl; … int n = tbl.Rows[i][j] Класс DataColumn • Ссылки на объекты данного класса можно получить из таблицы Columns с помощью индекса или с помощью названия колонки: DataSet ds = new DataSet(); // заполняем DataColumn col = ds.Columns["ContactName"]; DataColumn col = ds.Columns[2]; • Для получения в записи Row значению поля из колонки col можно получить следующим образом: DataRow r = ds.Table[0].Rows[0]; r[0] = 1; r[“CustomerID”] = 1; DataColumn col = ds.Columns[“CustomerID"]; r[col] = 1; Класс записей DataRow • Объект класса DataRow Запись (строка, data row) представляет собой запись данных из таблицы, в коллекции которой он хранится. • Записям таблицы DataTable хранятся в коллекции Rows. • Записи можно программно – добавлять к коллекции(add), – обновлять (update), – удалять из коллекции (delete). • Для получения доступа к конкретной записи используется операция [ ] с индексом. – Например, если dt является таблицей данных, то вторую запись можно получить следующим образом: DataRow row = dt.Rows[2]; Класс DataRelation • Для создания связи между первичным ключом и внешним ключом нужно создать объект класса DataRelation. – Например: DataColumn ParentColumn = ds.Tables[“Customers”].Columns[“CustomerID”]; DataColumn ChildColumn = ds.Tables[“Orders”].Columns[“CustomerID”]; DataRelation myRel = DataRelation(“CusOderRel”, ParentColumn, ChildColumn); • Затем созданное отношение между ключами нужно сохранить в коллекции Relations объекта DataSet, который содержит первичный ключ. – Например: myDS.Relations.Add(myRel); • Объект Row имеет методы – – GetChildRows(“имя таблицы”) GetParentRow(“имя таблицы”) Пример задания связи между записями • Задание связи Relationship с именем "RelOrdDet" между полями Orders.OrderID и OrderDetails. OrderID using System.Data; … DataRelation relOrdDet; DataColumn colMaster2; DataColumn colDetail2; colMaster2 = ds.Tables["Orders"].Columns["OrderID"]; colDetail2 = ds.Tables["OrderDetails"].Columns["OrderID"]; relOrdDet = new DataRelation("RelOrdDet",colMaster2,colDetail2); ds.Relations.Add(relOrdDet); • Связывание данных с помощью Relationship grdOrderDetails.DataSource = dsView; grdOrderDetails.DataMember = "Customers.RelCustOrd.RelOrdDet"; Объект DataSet Работа с данными в DataSet •Чтение данных •Изменение данных •Добавление записей в данные •Сортировка записей •Удаление данных Выборка массива записей из таблицы DataTable • Для фильтрации и сортировки записей в таблице используется метод Select() public DataRow[ ] Select( string filterExpression, string sort); // условие выборки // сортировка • Например: string filterStr = “id = 1”; string sortStr = “ . . . ”; DataRow[] sotr = emploees.Select(filterStr, sortStr); if(sotr.Length != 0) { …. } Добавления записи в таблицу DataTable • Для добавления новой записи к таблице необходимо выполнить следующие действия: – Создать с помощью метода NewRow() класса DataTable пустую запись имеющую структуру, соответствующую данной таблице (число и тип полей). • Например: DataRow row = tbl.NewRow (); – Занести данные в поля созданной записи. • Например: row[“id”] = 44; row[“authir”] = “Терентьев И.А.”; row[“year”] = 2003; – Сохранить новую запись в коллекции Rows таблицы. • Например: tbl.Rows.Add(row); Пример добавления записи в таблицу DataTable // создаем dataset DataSet ds = new DataSet(); // заполняем dataset данными для таблицы MyBooks … // получаем ссылку на таблицу MyBooks DataTable tbl = ds.Tables[“MyBooks”]; // создаем новую запись для данной таблицы DataRow row = tbl.NewRow (); // задаем значения полей row[“id”] = 44; row[“authir”] = “Терентьев И.А.”; row[“year”] = 2003; // заносим новую запись в таблицу tbl.Rows.Add(row); // сохраняем изменения в базу данных ad.Update(tbl); Удаление записей из таблицы DataTable • Для удаления записей можно использовать метод Remove() коллекции Rows: – Например: DataRow row = table.Rows[5]; table.Rows.Remove(row); • Для удаления записей можно использовать метод Delete() объекта DataRaw: – Например: DataRow row = table.Rows[5]; row.Delete(); Класс представление таблицы DataView • • Объект DataView это просмотр записей в объекте DataTable, с помощью фильтра и сортировки. Конструктор класса DataView: // myDT – таблица данных DataView myDV = new DataView(myDT); • Свойство сортировки элементов Sort: • Свойство отбора элементов (фильтр) RowFilter задается в виде условия, в представление будут выбираться только элементы, которые удовлетворяют данному условию. – myDV.Sort = “имя поля”; – Можно несколько полей (через запятую) – Можно в обратном порядке (DESC) – Например: myDV.Sort = “ID, Name”; – Например myDV.RowFilter = “City = ‘Tomsk’”; • Объект DataView можно связать с элементами графического интерфейса, например, DataGridView. – Например: DataGridView dgv = new DataGridView(); dgv.DataSource = myDV; Пример создания и связывания DataView с ЭУ DataGridView //создаем DataView DataView view = new DataView(employee); //задаем правило отбора view.RowFilter = "LastName like 'A%' and Salary > 15"; //задаем порядок сортировки view.Sort = "LastName ASC, FirstName ASC, Salary DESC"; //связываем элемент управления с DataView dgv.DataSource = view; Совпадение изменений БД • • • • • • • • При занесении изменений в БД могут возникать конфликты между разными пользователями. Например, два пользователя могут пытаться изменить одну и ту же запись в БД. Как БД разрешает такие конфликты? Данные какого пользователя должны заносить первыми, какого – вторыми, и должны ли они вовсе заноситься? Ответ не совсем однозначен. Все зависит от множества факторов. However, ADO.NET provides a fundamental level of concurrency control that’s designed to prevent update anomalies. The details are beyond the scope of this book, but the following is a good conceptual start. Basically, a dataset marks all added, modified, and deleted rows. If a row is propagated to the database but has been modified by someone else since the dataset was filled, the data manipulation operation for the row is ignored. This technique is known as optimistic concurrency and is essentially the job of the data adapter. When the Update method is called, the data adapter attempts to reconcile all changes. This works well in an environment where users seldom contend for the same data. This type of concurrency is different from what’s known as pessimistic concurrency, which locks rows upon modification (or sometimes even on retrieval) to avoid conflicts. Most database managers use some form of locking to guarantee data integrity. Disconnected processing with optimistic concurrency is essential to successful multitier systems. How to employ it most effectively given the pessimistic concurrency of DBMSs is a thorny problem. Don’t worry about it now, but keep in mind that many issues exist, and the more complex your application, the more likely you’ll have to become an expert in concurrency.