DiffGram是包含数据在编辑对话前后的XML文档。它可以包含修改、增加和删除数据的任何组合。DiffGram可以用作审计跟踪或用于提交/回滚(commit/rollback)过程。目前的大多数DBMS系统都内置了DiffGram,但如果使用一个没有这些功能的DBMS,或者XML是数据库,且用户没有DBMS,就可以自己执行提交/重新运行过程。
下面的代码显示了如何创建一个DiffGram,以及如何从DiffGram:pwd 中创建一个DataSet(这段代码在ADOSample6文件夹中)。
这段代码的起始部分您应很熟悉。定义和设置一个新的DataSet对象 ds,一个新的SqlConnection对象 conn和一个新的SqlDataAdapter对象 da。连接到数据库上,选择Products表中的所有行,创建一个新的DataTable,命名为products,把数据库中的数据加载到DataSet中:
private void button1_Click(object sender, System.EventArgs e)
{
//new DataSet
DataSet ds=new DataSet("XMLProducts");
//Make connection and load products rows
SqlConnection conn=new SqlConnection
(@"server=GLYNNJ_CS\NetSDK;uid=sa;pwd=;database=northwind");
SqlDataAdapter da=new SqlDataAdapter("SELECT * FROM products",conn);
//fill the DataSet
da.Fill(ds,"products");
//edit first row
ds.Tables["products"].Rows[0]["ProductName"]="NewProdName";
下面一部分代码完成了两个任务。首先,把第一行的ProductName列改为NewProdName。其次,在DataTable中创建一个新行,设置列值,最后把新的数据行添加到DataTable中:
//add new row
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;
ds.Tables["products"].Rows.Add(dr);
这是代码中比较有意义的一部分。首先用WriteXmlSchema编写模式,这是非常重要的,因为没有模式,就不能读取DiffGram。带有XmlWriteMode.DiffGram参数的WriteXml创建了DiffGram。下一行代码接受我们进行的修改。在调用AcceptChanges前创建DiffGram也是很重要的,否则数据在修改前和修改后就不会有任何区别。
//Write the Schema
ds.WriteXmlSchema("..\\..\\..\\diffgram.xsd");
//generate the DiffGram
ds.WriteXml("..\\..\\..\\diffgram.xml",XmlWriteMode.DiffGram);
ds.AcceptChanges();
//load data into grid
dataGrid1.DataSource=ds;
dataGrid1.DataMember="products";
//new XmlDataDocument
doc=new XmlDataDocument(ds);
//load the productnames in the list
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。在本例中,就是products表的一个复制。现在可以读取DiffGram,该DiffGram不包含任何模式信息,所以在调用ReadXml方法前创建和准备好DataTable就是非常重要的。
下面是DiffGram的一个示例(diffgram.xml):
<?xml version="1.0" standalone="yes"?>
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
<XMLProducts>
<products diffgr:id="products1" msdata:rowOrder="0"
diffgr:hasChanges="modified">
<ProductID>1</ProductID>
<ProductName>NewProdName</ProductName>
<SupplierID>1</SupplierID>
<CategoryID>1</CategoryID>
<QuantityPerUnit>10 boxes x 20 bags</QuantityPerUnit>
<UnitPrice>18</UnitPrice>
<UnitsInStock>39</UnitsInStock>
<UnitsOnOrder>0</UnitsOnOrder>
<ReorderLevel>10</ReorderLevel>
<Discontinued>false</Discontinued>
</products>
...
<products diffgr:id="products78" msdata:rowOrder="77"
diffgr:hasChanges="inserted">
<ProductID>100</ProductID>
<ProductName>This is the new product</ProductName>
<SupplierID>12</SupplierID>
<CategoryID>2</CategoryID>
<QuantityPerUnit>12</QuantityPerUnit>
<UnitPrice>23</UnitPrice>
<UnitsInStock>5</UnitsInStock>
<UnitsOnOrder>0</UnitsOnOrder>
<ReorderLevel>1</ReorderLevel>
<Discontinued>false</Discontinued>
</products>
</XMLProducts>
<diffgr:before>
<products diffgr:id="products1" msdata:rowOrder="0">
<ProductID>1</ProductID>
<ProductName>Chai</ProductName>
<SupplierID>1</SupplierID>
<CategoryID>1</CategoryID>
<QuantityPerUnit>10 boxes x 20 bags</QuantityPerUnit>
<UnitPrice>18</UnitPrice>
<UnitsInStock>39</UnitsInStock>
<UnitsOnOrder>0</UnitsOnOrder>
<ReorderLevel>10</ReorderLevel>
<Discontinued>false</Discontinued>
</products>
</diffgr:before>
</diffgr:diffgram>
注意每个DataTable行是如何重复的,每个<products>元素都有一个diffgr:id属性(为节省空间,只列出了第一个和最后一个<products>元素)。Diffgr是urn:schemas-microsoft-com:xml- diffgram-v1的命名空间前缀。对于修改和插入的行,ADO.NET增加了一个diffgr:hasChanges属性。在<XMLProducts>元素的后面还有一个ffgr:before>元素,它包含一个<products>元素,表示被修改的行以前的内容。显然,插入的行没有任何“以前”的值,所以在<diffgr:before>中没有元素。
在把DiffGram读入DataTable后,它就处于数据被修改之后,但在调用AcceptChanges之前的状态。此时可以通过调用RejectChanges方法撤销所进行的修改。通过查看DataRow.Item属性,以及它的参数DataRowVersion.Original 或 DataRowVersion.Current,就可以确定DataTable在修改前后的值。
如果有一系列的DiffGram,能以合适的顺序重复应用它们是非常重要的。我们不想回退多次迭代所作的改变。但是如果使用的DBMS没有提供这些功能,可以使用DiffGram作为一种记录的格式,或用于审查。