На самом деле даже без провайдера 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.
|