Страница 7 из 15
XML-представление наборов данных в ADO .NET На самом деле даже без провайдера SQLXMLOLEDB и SQLXML веб-релизов в Visual Studio .Net (точнее, в ADO.Net) имеются достаточно мощные средства для представления реляционных наборов данных в виде XML, и наоборот, XML в реляционном виде. Типовой сценарий работы выглядит следующим образом: получить внутри объекта DataSet таблицы как результаты запросов к источнику данных (возможно, к разным), связать их между собой на основе объектов DataRelation и создать XML-представление DataSet'a при помощи XmlDataDocument, как показано в Скрипте 6. using System.Data; using System.Data.OleDb; using System.Xml; ... static void Transform_ADONetDataSet_Xml() { DataSet ds = new DataSet("Новый набор данных на клиенте"); ( new OleDbDataAdapter("SELECT CustomerID, ContactName, ContactTitle FROM Customers", ConstDeclarations.ConnectionString)).Fill(ds, "Клиентская копия табл.клиентов"); ( new OleDbDataAdapter("SELECT OrderID, CustomerID, OrderDate FROM Orders", ConstDeclarations.ConnectionString)).Fill(ds, "Клиентская копия табл.заказов"); ds.Relations.Add("Джойн двух копий на клиенте", ds.Tables["Клиентская копия табл.клиентов"].Columns["CustomerID"], ds.Tables["Клиентская копия табл.заказов"].Columns["CustomerID"]) .Nested = true; XmlDataDocument xml = new XmlDataDocument(ds); FileInfo f = new FileInfo("..\\Results\\ADONetDataSet.xml"); xml.Save(f.FullName); ... } Скрипт 6 Результирующий XML можно видеть на рис.2. <Новый_x0020_набор_x0020_данных_x0020_на_x0020_клиенте> <Клиентская_x0020_копия_x0020_табл.клиентов> <CustomerID>ALFKI </CustomerID> <ContactName>Maria Anders </ContactName> <ContactTitle>Sales Representative </ContactTitle> <Клиентская_x0020_копия_x0020_табл.заказов> <OrderID>10643 </OrderID> <CustomerID>ALFKI </CustomerID> <OrderDate>1997-08-25T00:00:00.0000000+04:00 </OrderDate> </Клиентская_x0020_копия_x0020_табл.заказов> <Клиентская_x0020_копия_x0020_табл.заказов> <OrderID>10692 </OrderID> ... Рис. 2 По умолчанию из DataSet будет сгенерирован документ, в котором каждой записи DataRow соответствует элемент с именем DataTable. Значения полей присутствуют в виде подэлементов DataRow с названиями соответствующих полей DataColumns. Поскольку DataSet предполагает отсоединенный режим работы, отношения между таблицами в источнике (в БД на SQL Server) не принимаются во внимание. Так, несмотря на связывание таблиц в запросе: (new OleDbDataAdapter("SELECT c.ContactName, c.ContactTitle, o.OrderDate FROM Customers c INNER JOIN Orders o ON c.CustomerID = o.CustomerID", Connection)).Fill(ds); с точки зрения DataSet это плоское множество записей, потому что связи отработал сервер и прислал в DataSet готовый табличный результат. Для образования иерархического XML-документа, где записи дочерней таблицы являются вложенными элементами родительской, отношение между таблицами нужно указывать явно в DataSet.Relations, при этом свойство .Nested объекта DataRelation должно быть выставлено в true. (Иначе записи из родительской и дочерней таблиц будут перечислены друг за другом на одном и том же уровне иерархии). Класс XmlDataDocument является производным от DOMовского XmlDocument, поэтому с его помощью над DataSet'ом можно выполнять все стандартные XML-операции: XPath-запросы, XSL-преобразования и т.д. static void Update_ADONetDataSet_Xml() { OleDbConnection cn = new OleDbConnection("Provider=SQLOLEDB;..."); DataSet ds = new DataSet(); OleDbDataAdapter daCust = new OleDbDataAdapter("SELECT CustomerID, ContactName, ContactTitle FROM Customers", cn); //Создаем UpdateCommand вручную daCust. UpdateCommand = new OleDbCommand("UPDATE Customers SET ContactName = ?, ContactTitle = ? WHERE CustomerID = ?", cn); daCust.UpdateCommand.Parameters.Add("@ContactName", OleDbType.VarChar, 40, "ContactName"); daCust.UpdateCommand.Parameters.Add("@ContactTitle", OleDbType.VarChar, 40, "ContactTitle"); daCust.UpdateCommand.Parameters.Add("@CustomerID", OleDbType.Char, 5, "CustomerID"); daCust.Fill(ds, "Cust"); OleDbDataAdapter daOrds = new OleDbDataAdapter("SELECT OrderID, CustomerID, OrderDate FROM Orders", cn); //Создаем UpdateCommand автоматически OleDbCommandBuilder cbOrds = new OleDbCommandBuilder(daOrds); daOrds.Fill(ds, "Ords"); ds.Relations.Add("Джойн двух копий на клиенте", ds.Tables["Cust"].Columns["CustomerID"], ds.Tables["Ords"].Columns["CustomerID"]).Nested = true; ds.EnforceConstraints = false; XmlDataDocument xml = new XmlDataDocument(ds); //Эквивалентно ds.Tables["Cust"].Select("CustomerID = 'ALFKI'")[0]["ContactName"] = "Maria Anders"; xml. SelectSingleNode("//Cust[CustomerID='ALFKI']/ContactName").InnerText = "Maria Anders"; xml. SelectSingleNode("//Cust[CustomerID='ALFKI']/Ords[OrderID=10643]/OrderDate").InnerText = "1997-08-25T00:00:00.0000000+04:00"; daCust. Update(ds.Tables[1]); daCust. Update(ds.Tables[0]); ... } Скрипт 7 Скрипт 7 демонстрирует, что данные источника можно модифицировать не только напрямую через DataSet (ds.Tables[<Имя или номер таблицы в коллекции>].Rows[<Номер записи в коллекции>][<Имя или номер поля в коллекции>] = …), но и через его XML-представление. В примере изменяются значений некоторых XPath-узлов объекта XmlDataDocument. Эти изменения отражаются в DataSet'е, над которым построен данный XmlDataDocument, а поскольку у соответствующих DataAdapter'ов таблиц определены UpdateCommand'ы, то они будут транслированы далее в источник данных (SQL Server). Обратное тоже верно. Т.е. в DataSet можно подгрузить XML-документ, который затем читать и модифицировать реляционными операциями. В Скрипте 8 мы получаем схему построенного в предыдущем примере XML-файла при помощи утилиты xsd.exe, входящей в состав .NET Framework, читаем ее в XmlDataDocument и загружаем туда данные из этого документа. На основе XSD-схемы ADO.Net создает DataSet эквивалентной реляционной структуры, так что становится возможным обращаться и модифицировать XML-документ, как если бы он был совокупностью связанных таблиц. static void Update_XML_ADONetDataset() { FileInfo f = new FileInfo("..\\Results\\ADONetDataSet.xml"); Process.Start("xsd.exe", f.FullName + " /o:..\\Results"); XmlDataDocument xml = new XmlDataDocument(); xml.DataSet.ReadXmlSchema(Path.ChangeExtension(f.FullName, ".xsd")); xml.Load(f.FullName); xml.DataSet.Tables["Cust"].Select("CustomerID='ALFKI'")[0]["ContactName"] = "Абра Кадабра"; xml.DataSet.Tables["Ords"].Select("OrderID=10643")[0]["OrderDate"] = DateTime.Now; ... } Скрипт 8 Неплохим иллюстративным примером было бы приложение, которое документирует пользовательские библиотеки классов .Net в базе данных. Определения классов и объекты сохраняются в виде XSD-схем и XML-документов (см. System.Xml.Serialization), а на их основе, в свою очередь, при помощи рассмотренного соответствия реляционного и XML-представлений, которое обеспечивает ADO.Net, создается и наполняется БД. В качестве самостоятельного упражнения вы можете попробовать сами написать такое приложение и назвать его, скажем, Cheops. Впрочем, я отвлекся. Чрезвычайно мощная и развитая функциональность ADO.Net по своей сути представляет собой результат эволюции простой возможности сохранения ADODB.Recordset в формате XML на стороне клиента, с которой начинался наш разговор (см. п.2). Вернемся, тем не менее, к основной теме - поддержке XML в SQL Server. |