DekGenius.com
[ Team LiB ] Previous Section Next Section

Recipe 8.8 Creating an XML File That Shows Changes Made to a DataSet

Problem

When you use the GetXML( ) method of the DataSet, you may see only the current values in the DataSet. You want to get the original values and see which rows were added, edited, or deleted.

Solution

Create an XML DiffGram, which is a document that details the modifications made to a DataSet.

The sample code contains two event handlers and a single method:

Form.Load

Sets up the sample by loading the Categories table from Northwind into a DataSet and displays the DiffGram for the DataSet before modification.

Make Changes Button.Click

Deletes the first row, modifies the second row, inserts a row before the first row, and adds a row to the end of the Categories DataTable

DisplayDiffGram( )

This method outputs the DiffGram for the DataSet to both a file and to a textbox on the form.

The C# code is shown in Example 8-12.

Example 8-12. File: XmlDiffgramForm.cs
// Namespaces, variables, and constants
using System;
using System.Configuration;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Data;
using System.Data.SqlClient;

// Table name constants
private const String CATEGORIES_TABLE    = "Categories";

// Field name constants
private const String CATEGORYNAME_FIELD  = "CategoryName";

private const String XMLDIFFGRAMFILENAME =
    ConfigurationSettings.AppSettings["Temp_Directory"] +
    "CategoriesDiffgram.xml";

private DataSet ds;

//  . . . 

private void XmlDiffgramForm_Load(object sender, System.EventArgs e)
{
    // Select fields from Categories table without the Picture BLOB.
    String sqlText = "SELECT CategoryID, CategoryName, Description " +
        "FROM Categories";

    // Load the Categories table into the DataSet.
    SqlDataAdapter da = new SqlDataAdapter(sqlText,
        ConfigurationSettings.AppSettings["Sql_ConnectString"]);
    ds = new DataSet( );
    da.Fill(ds, "Categories");

    DisplayDiffGram( );
}

private void makeChangesButton_Click(object sender, System.EventArgs e)
{
    // Disable the make changes button.
    makeChangesButton.Enabled = false;

    DataTable dt = ds.Tables["Categories"];

    // Delete the first row.
    dt.Rows[0].Delete( );

    // Modify the second row.
    dt.Rows[1][CATEGORYNAME_FIELD] += "New ";

    // Insert a row.
    DataRow row = dt.NewRow( );
    row.ItemArray = new object[] {0, "New Category0",
        "New Category0 Description"};
    dt.Rows.InsertAt(row, 0);

    // Add a row.
    dt.Rows.Add(new object[] {9, "New Category9",
        "New Category9 Description"});

    DisplayDiffGram( );
}

private void DisplayDiffGram( )
{
    // Write the XML diffgram to a memory stream.
    MemoryStream ms = new MemoryStream( );
    ds.WriteXml(ms, XmlWriteMode.DiffGram);
    
    // Write the memory stream to a file.
    FileStream fs = new FileStream(XMLDIFFGRAMFILENAME, FileMode.Create,
        FileAccess.Write);
    ms.WriteTo(fs);
    fs.Close( );

    // Display the XML DiffGram.
    byte[] result = ms.ToArray( );
    ms.Close( );
    resultTextBox.Text = Encoding.UTF8.GetString(result,0,result.Length);
}

Discussion

A DiffGram is an XML format used to specify both the original and current values for the contents of a DataSet. It does not include any schema information. The DiffGram is also the primary serialization format used by the .NET Framework to persist and serialize a DataSet. The DiffGram format is XML-based, making it platform and application independent. It is not, however, widely used or understood outside of Microsoft .NET applications.

The DiffGram format is divided into three sections—current, original, and errors—as shown in the following example:

<?xml version="1.0"?>
<diffgr:diffgram
    xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
    xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">

    <DataInstanceName>
         . . . 
    </DataInstanceName>

    <diffgr:before>
         . . . 
    </diffgr:before>

    <diffgr:errors>
         . . . 
    </diffgr:errors>
</diffgr:diffgram>

Here are descriptions of the three DiffGram sections:

<DataInstanceName>

The DataInstanceName is the name of the DataSet or DataTable. This block contains the current version of the data containing all modifications. Modified elements are identified with the diffgr:hasChanges="modified" annotation while new elements are identified with the diffgr:hasChanges="inserted" annotation. Deleted elements are not annotated, rather, they appear only in the <diffgr:before> section.

<diffgr:before>

This section contains the original version of the elements that have been modified or deleted. Elements in this section are matched to elements in the <DataInstanceName> section using the diffgr:id annotation with matching values.

<diffgr:errors>

This section contains error information for an element in the <DataInstanceName> section. Elements in this section are matched to elements in the <DataInstanceName> section using the diffgr:id annotation with matching values.

The example loads all Categories from Northwind into a DataTable called Categories in a DataSet. The first row is deleted, the second is modified, a row is inserted before the first row, and a row is added to the end of the DataTable. After these modifications, the DiffGram for the DataSet is:

<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
    xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
  <NewDataSet>
    <Categories diffgr:id="Categories9" msdata:rowOrder="0"
        diffgr:hasChanges="inserted">
      <CategoryID>0</CategoryID>
      <CategoryName>New Category0</CategoryName>
      <Description>New Category0 Description</Description>
    </Categories>
    <Categories diffgr:id="Categories2" msdata:rowOrder="2"
        diffgr:hasChanges="modified">
      <CategoryID>2</CategoryID>
      <CategoryName>CondimentsNew </CategoryName>
      <Description>Sweet and savory sauces, relishes, spreads, and 
        seasonings</Description>
    </Categories>
    <Categories diffgr:id="Categories3" msdata:rowOrder="3">
      <CategoryID>3</CategoryID>
      <CategoryName>Confections</CategoryName>
      <Description>Desserts, candies, and sweet breads</Description>
    </Categories>

<!--  . . .  -->

    <Categories diffgr:id="Categories8" msdata:rowOrder="8">
      <CategoryID>8</CategoryID>
      <CategoryName>Seafood</CategoryName>
      <Description>Seaweed and fish</Description>
    </Categories>
    <Categories diffgr:id="Categories10" msdata:rowOrder="9"
        diffgr:hasChanges="inserted">
      <CategoryID>9</CategoryID>
      <CategoryName>New Category9</CategoryName>
      <Description>New Category9 Description</Description>
    </Categories>
  </NewDataSet>
  <diffgr:before>
    <Categories diffgr:id="Categories1" msdata:rowOrder="1">
      <CategoryID>1</CategoryID>
      <CategoryName>Beverages</CategoryName>
      <Description>Soft drinks, coffees, teas, beers, and ales</Description>
    </Categories>
    <Categories diffgr:id="Categories2" msdata:rowOrder="2">
      <CategoryID>2</CategoryID>
      <CategoryName>Condiments</CategoryName>
      <Description>Sweet and savory sauces, relishes, spreads, 
      and seasonings</Description>
    </Categories>
  </diffgr:before>
</diffgr:diffgram>

As expected, the DiffGram contains both the annotation indicating inserted and modified records. The <diffgr:before> section contains the original record for the deleted records with CategoryID=1 and the modified record with CategoryID=2.

As shown in the example, a DiffGram is written for the DataSet by specifying an XmlWriteMode of DiffGram when calling the WriteXml( ) method of the DataSet. The GetXml( ) method cannot be used to generate a DiffGram. A DataSet can be loaded from an XML DiffGram by specifying an XmlWriteMode of DiffGram when calling the ReadXml( ) method.

    [ Team LiB ] Previous Section Next Section