"C# для профессионалов. Том II" - читать интересную книгу автора (Робинсон Симон, Корнес Олли, Глинн Джей,...)

Запись и чтение DiffGram

DiffGram является документом XML, который содержит данные до и после сеанса редактирования, включающего любую комбинацию изменений данных, добавлений и удалений. DiffGram может использоваться как аудиторский журнал или для процесса подтверждения/отката. Большинство систем DBMS сегодня имеют такое встроенное средство. Но если придется работать с DBMS, которая не имеет таких свойств или если хранилищем данных является XML и отсутствует DBMS, то можно будет самостоятельно реализовать свойства подтверждения/отката.

Далее представлен код, показывающий, как DiffGram создается и как DataSet можно создать из DiffGram:pwd (он находится в папке ADOSample6). Начальная часть этого кода должна быть уже знакома. Мы определяем и задаем новый объект DataSet, ds, новый объект SqlConnection, conn и новый объект SqlDataAdapter, da. Мы соединяемся с базой данных, выбираем все строки из таблицы Products, создаем новый объект DataTable с именем products и загружаем данные из базы данных в DataSet:

private void button1_Click(object sender, System.EventArgs e) {

 // новый объект DataSet

 DataSet ds=new DataSet("XMLProducts");

 // Сделать соединение и загрузить строки продуктов

 SqlConnection conn=

  new SqlConnection(@"server=GLYNNJ_CS\NetSDK;uid=sa;pwd=;database=northwind");

 SqlDataAdapter da=new SqlDataAdapter("select * from products", conn);

 // заполнить DataSet

 da.Fill(ds, "products");

 // редактируем первую строку

 ds.Tables["products"].Rows[0]["ProductName"] = "NewProdName";

В следующем разделе мы сделаем следующие преобразования. Во-первых, изменим столбец ProductName в первой строке на NewProdName. Во-вторых, создадим новую строку в DataTable, задавая значения столбцов и добавляя в конце новую строку данных в DataTable.

 // добавить новую строку

 DataRow dr=ds.Tables["products"].NewRow();

 dr["ProductId"]=100;

 dr["CategoryId"]=2;

 dr["Discontinued"]=false;

 dr["ProductName"]="This is the new product";

 dr["QuantityPerUnit"]=12;

 dr["ReorderLevel"]=1;

 dr["SupplierId"]=12;

 dr["UnitPrice"]=23;

 dr["UnitsInStock"]=5;

 dr["UnitsOnOrder"]=0;

 Tables["products"].Rows.Add(dr);

Это интересная часть кода. Прежде всего записывается схема с помощью WriteXmlSchema. Это важно, так как нельзя будет заново считать в DiffGram без схемы. WriteXml с переданным в него параметром XmlWriteMode.DiffGram создает в действительности DiffGram. Следующая строка принимает сделанные изменения. Важно то, что DiffGram создается до вызова AcceptChanges, иначе не будет никакого различия между состоянием до того и после.

// записать схему

ds.WriteXmlSchema("..\\..\\..\\diffgram.xsd");

// создать DiffGram

ds.WriteXml("..\\..\\..\\diffgram.xml", XmlWriteMode.DiffGram);

ds.AcceptChanges();

// загрузить данные в сетку

dataGrid1.DataSource=ds;

dataGrid1.DataMember="products";

// новый объект XmlDataDocument

doc=new XmlDataDocument(ds);

// загрузить имена продуктов в список

XmlNodeList nodeLst=doc.SelectNodes("//ProductName");

foreach (XmlNode nd in nodeLst) listBox1.Items.Add(nd.InnerXml);

Чтобы вернуть данные в множество DataSet, можно сделать следующее:

DataSet dsNew = new DataSet();

dsNew.ReadXmlSchema("..\\..\\..\\diffgram.xsd");

dsNew.XmlRead("..\\..\\..\\diffgram.xml", XmlReadMode.DiffGram);

В этом примере создается новый объект множества данных DataSet, dsNew. Вызов метода ReadXmlSchema создает новый объект DataTable на основе информации схемы. В данном случае он будет клоном DataTable продуктов. Теперь можно считать в DiffGram. DiffGram не содержит информации о схеме, поэтому важно, чтобы объект DataTable был создан и готов к использованию до вызова метода ReadXml. Вот образец того, как выглядит DiffGram (diffgram.xml):

lt;?xml version="1.0" standalone="yes"?gt;

 lt;diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"gt;

  lt;XMLProductsgt;

   lt;products diffgr:id="products1" msdata:rowOrder="0" diffgr:hasChanged="modified"gt;

    lt;ProductIDgt;1lt;/ProduсtIDgt;

    lt;ProductNamegt;NewProdNamelt;/ProductNamegt;

    lt;SupplierIDgt;1lt;/SupplierIDgt;

    lt;CategoryIDgt;1lt;/CategoryIDgt;

    lt;QuantityPerUnitgt;10 boxes x 20 bagslt;/QuantityPerUnitgt;

    lt;UnitPricegt;18lt;/UnitPricegt;

    lt;UnitsInStockgt;39lt;/UnitsInStockgt;

    lt;UnitsOnOrdergt;0lt;/UnitsOnOrdergt;

    lt;ReorderLevelgt;10lt;/ReorderLevelgt;

    lt;Discontinuedgt;falselt;/Discontinuedgt;

   lt;/productsgt;

   lt;products diffgr:id="products2" msdata:rowOrder="1"gt;

    lt;ProductIDgt;2lt;/ProductIDgt;

    lt;ProduсtNamegt;Changlt;/ProductNamegt;

    lt;SupplierIDgt;1lt;/SupplierIDgt;

    lt;CategoryIDgt;1lt;/CategoryIDgt;

    lt;QuantityPerUnitgt;24 - 12 oz bottleslt;/QuantityPerUnitgt;

    lt;UnitPricegt;19lt;/UnitPricegt;

    lt;UnitsInStockgt;17lt;/UnitsInStockgt;

    lt;UnitsOnOrdergt;40lt;/UnitsOnOrdergt;

    lt;ReorderLevelgt;25lt;/ReorderLevelgt;

    lt;Discontinuedgt;falselt;/Discontinuedgt;

   lt;/productsgt;

   lt;products diffgr:id="products78" msdata:rowOrder="77" diffgr:hasChanges="inserted"gt;

    lt;ProductIDgt;100lt;/ProductIDgt;

    lt;ProductNamegt;This is a new productlt;/ProductNamegt;

    lt;SupplierIDgt;12lt;/SupplierIDgt;

    lt;CategoryIDgt;2lt;/CategoryIDgt;

    lt;QuantityPerUnitgt;12lt;/QuantityPerUnitgt;

    lt;UnitPricegt;23lt;/UnitPricegt;

    lt;UnitsInStockgt;5lt;/UnitsInStockgt;

    lt;UnitsOnOrdergt;0lt;/UnitsOnOrdergt;

    lt;ReorderLevelgt;1lt;/ReorderLevelgt;

    lt;Discontinuedgt;falselt;/Discontinuedgt;

   lt;/productsgt;

  lt;/XMLProductsgt;

  lt;diffgr:beforegt;

   lt;products diffgr:id="products1" msdata:rowOrder="0"gt;

    lt;ProductIDgt;1lt;/ProductIDgt;

    lt;ProductNamegt;Chai lt;/ProductNamegt;

    lt;SupplierIDgt;1lt;/SupplierIDgt;

    lt;CategoryIDgt;1lt;/CategoryIDgt;

    lt;QuantityPerUnitgt;10 boxes x 20 bugs lt;/QuantityPerUnitgt;

    lt;UnitPricegt;18lt;/UnitPricegt;

    lt;UnitsInStockgt;39lt;/UnitsInStockgt;

    lt;UnitsOnOrdergt;0lt;/UnitsOnOrdergt;

    lt;ReorderLevelgt;10lt;/ReorderLevelgt;

    lt;Discontinuedgt;falselt;/Discontinuedgt;

   lt;/productsgt;

  lt;/diffgr:beforegt;

lt;/diffgr:diffgramgt;

Заметим, каким образом повторяется каждая строка DataTable, и что существует атрибут diffgr:id для каждого элемента lt;productsgt;. diffgr является префиксом пространства имен для urn:schemas-microsoft-com:xml-diffgram-v1. Для модифицированной строки и для вставленной строки ADO.NET добавляет атрибут diffgr:hasChanges. Здесь есть также элемент lt;diffgr:beforegt; после элемента lt;XMLProductsgt;, который содержит элемент lt;productsgt;, указывающий на предыдущее содержание всех модифицированных строк. Для добавленной строки не существует "before", поэтому здесь отсутствует элемент lt;diffgr:beforegt;, однако он присутствует для модифицированной строки.

После того как DiffGram считан в DataTable, он оказывается в состоянии, в котором он был бы после выполнения изменений в данных перед вызовом AcceptChanges. В этом месте можно на самом деле откатить изменения, вызывая метод RejectChanges. Проверяя свойство DataRow.Item и передавая либо DataRowVersion.Original, либо DataRowVersion.Current, можно увидеть значения в DataTable перед и после изменений.