The DiffGram Viewer Application

To fully demonstrate the workings of XML DiffGrams, nothing is better than taking a DataSet object, entering some changes, and seeing how the corresponding DiffGram representation varies. For this purpose, I created the DiffGram Viewer Windows Forms application, shown in Figure 10-3. The application is available in this book’s sample files.

Figure 10-3. The DiffGram Viewer sample application in action.


This application executes a couple of SQL commands to obtain a DataSet object filled with two tables—Employees and Territories. The names of the DataSet object and the in-memory tables can be changed at will using text boxes. Next the application creates a relation between the tables, sets the nesting property to true, and creates the DiffGram.

The DiffGram is created using an in-memory string writer, and the output text is written to a multiline, read-only text box. Clicking the Edit button opens a new form with a DataGrid control for editing rows. The DataGrid control is bound to the DataSet object generated by the query, and is shown in Figure 10-4.

Figure 10-4. At the end of the editing phase, the updated DataSet object is resaved as a DiffGram and the pending changes are displayed.


The child form allows you to set errors and enter any type of changes. When the form is dismissed, the main application automatically saves the bound DataSet object back to a DiffGram and refreshes the user interface. As a result, you can easily test the DiffGram and view how the output varies after data changes.

A nice feature of the DiffGram Viewer application is that it lets you toggle the DiffGram view between plain text and XML. The XML view is provided by Internet Explorer, as shown in Figure 10-5.

The DiffGram Viewer application makes use of the WebBrowser ActiveX control, which is imported almost seamlessly by Microsoft Visual Studio .NET. The following code shows how to refresh such a Web view. To view the DiffGram using the WebBrowser control, the DiffGram must first be saved to disk as a temporary XML file.

void RefreshWebBrowser()
{ 
    // Url is a form property that points to the DiffGram file
    object o1=null, o2=null, o3=null, o4=null;
    WebBrowser.Navigate(Url, ref o1, ref o2, ref o3, ref o4);
}

Figure 10-5. The DiffGram displayed in Internet Explorer.


A DiffGram has no trace of relationships between tables unless the Nested property of the DataRelation object is set to true. This system is reasonable in light of what we saw in Chapter 9. ADO.NET serializes information about tables relationships using XML Schema constructs. Because a DiffGram does not include schemas, it can’t contain static information about table relationships. When the Nested property is set to true, the parent/child relationship is expressed by grouping child rows as a subtree of the parent row.

Persisting a DataSet Object to a DiffGram

A DiffGram is programmatically created by calling the WriteXml method of the DataSet class. To save data to a DiffGram, however, you must explicitly set the XmlWriteMode argument of the method to the flag XmlWriteMode.DiffGram, as shown in the following code. The XML data created in this way does not include schema information. We’ll return to this important point in the section “Schema Information in the DiffGram,” on page 461.

// Prepare the output stream
StreamWriter sw = new StreamWriter(fileName);
XmlTextWriter writer = new XmlTextWriter(sw);
writer.Formatting = Formatting.Indented; 

// Create the diffgram
ds.WriteXml(writer, XmlWriteMode.DiffGram); 
writer.Close();

The DiffGram contains all the rows from all the tables found in the DataSet object. You can create ad hoc subsets of the DataSet object to narrow the information being saved. In particular, you can use the DataSet object’s GetChanges method to save only those rows that contain uncommitted changes, as shown here:

DataSet dsChanges = ds.GetChanges();
dsChanges.WriteXml(writer, XmlWriteMode.DiffGram);

The GetChanges method also has a few overloads that let you control the type of changes you are interested in. For example, the following code prepares a DiffGram containing only the rows that have been inserted:

DataSet dsChanges = ds.GetChanges(DataRowState.Added);
dsChanges.WriteXml(writer, XmlWriteMode.DiffGram);

Loading a DataSet Object from a DiffGram

When you try to build a DataSet object from an XML DiffGram, you must first ensure that the target DataSet object has a schema that is compatible with the data in the DiffGram.

In no case does the ReadXml method—the only DataSet method that can load a DiffGram—infer the schema or extend with new elements an existing schema. ReadXml works by merging the rows read from the DiffGram with existing rows in the DataSet object. The DiffGram row identifier (the diffgr:id attribute) is used to pair DiffGram and DataSet object rows.

Any incompatibility between the current schema of the DataSet object and the data in the DiffGram throws an exception and causes the merge operation to fail. As a result, you can’t load a DiffGram into an empty, newly created DataSet object. You can create the target DataSet object simply by cloning an existing object that you know has the correct schema. Or, more realistically, you might want to read the schema from an external support using the Read­Xml­Schema method. The following code snippet shows how to create a DiffGram and its schema in distinct files:.

// Prepare the output stream for the DiffGram
StreamWriter diffStrm = new StreamWriter(diffgramFile);
XmlTextWriter writer = new XmlTextWriter(diffStrm);
writer.Formatting = Formatting.Indented; 

// Create the diffgram from the ds DataSet
ds.WriteXml(writer, XmlWriteMode.DiffGram); 
writer.Close();

// Prepare the output stream for the schema
StreamWriter xsdStrm = new StreamWriter(schemaFile);
XmlTextWriter writer = new XmlTextWriter(xsdStrm);
writer.Formatting = Formatting.Indented; 

// Create the schema from the ds DataSet
ds.WriteXmlSchema(writer); 
writer.Close();

The schema written with WriteXmlSchema is an XML Schema and includes table, relation, and constraint definitions.

Schema Information in the DiffGram

In general, the schema and the data should be kept in separate files and handled as truly independent entities. The schema and the data are tightly coupled, and if serialization is involved, you might want to consider putting schema information in-line in the data.

In the .NET Framework, the WriteXml method does not provide the capability to include schema information along with the data. This is more of a design choice than an objective difficulty. An indirect confirmation comes from the XML string you get from a Web service method that returns a DataSet object. The output is a DiffGram extended with schema information, as shown here:

<DataSet>
  <xs:schema> ... </xs:schema>
  <diffgr:diffgram ... > 
  ...
  </diffgr:diffgram>
</DataSet>

By design, the current DiffGram implementation does not include schema information. However, I can’t see any reason for not providing the schema option in future versions. The DataSet representation you get from a Web service method offers a glimpse of what could be a possible enhancement of the DiffGram format. Technically speaking, the Web service serialization of a DataSet object is not a DiffGram, but rather a new XML format that incorporates a DiffGram. In addition, this new format is not produced by WriteXml but comes care of the XML serializer—a different breed of data formatter that we’ll explore in Chapter 11.

Creating DiffGrams with Schemas

The DiffGram Viewer application includes a Save With Schema check box that enables you to persist the DataSet object using the XML serializer. The final output, shown in Figure 10-6, is the same as you would obtain through a Web service. (This happens because .NET Framework Web services are actually serviced by the XML serializer.)

Figure 10-6. A DataSet object serialized through the XML serializer class.


The code that saves the DataSet object to a DiffGram changes as follows:

StreamWriter sw = new StreamWriter(fileName);
XmlTextWriter writer = new XmlTextWriter(sw);
writer.Formatting = Formatting.Indented; 

// Create the diffgram
if (!bUseSchema)
    ds.WriteXml(writer, XmlWriteMode.DiffGram); 
else
{
    XmlSerializer ser = new XmlSerializer(typeof(DataSet));
    ser.Serialize(writer, ds);
}
writer.Close();

If schema information must be included, the application makes use of the XML serializer defined in the System.Xml.Serialization namespace. The constructor of the XML serializer takes the type of the data to process as an argument and invokes the Serialize method. In Chapter 11, I’ll unveil what really happens at this stage and how the XML serializer sets itself up to work on a particular data type. For now, suffice to say that once the instance of the serializer has been configured, you simply call the Serialize method on the object instance to be persisted. When the object is a DataSet, the output is a DiffGram and a schema—that is, an XML Schema and a DiffGram rooted under a common node. The name of the root matches the name of the type being serialized (for example, DataSet) and can’t be modified programmatically.

Loading DiffGrams with Schemas

To read back a DiffGram and a schema into a DataSet object, you call the XML deserializer. Deserialization is the process of reading an XML document and building an object instance that coincides with a given XML Schema. With DataSet objects, the schema and the data are stored as distinct nodes under a common root. The data is expressed as a DiffGram.

To set up the serializer, follow the same steps as in the previous section. You instantiate the XmlSerializer class and pass the type of the object to process, as shown here:

XmlSerializer ser = new XmlSerializer(typeof(DataSet));
DataSet dsNew = (DataSet) ser.Deserialize(writer, ds);

To deserialize, call the Deserialize method and cast the object you get to the DataSet type.

DiffGrams and Remoting

When a DataSet object is serialized to a .NET Framework formatter, it directly controls the format of its data through the methods of the ISerializable interface. In particular, a serializable class implements the GetObjectData method, as shown here:

void GetObjectData(SerializationInfo info,
    StreamingContext context)

The class passes its data to the formatter by adding entries to the SerializationInfo object using the AddValue method. A DataSet object serializes itself by adding a couple of entries, as shown in the following pseudocode:

info.AddValue("XmlSchema", this.GetXmlSchema());
this.WriteXml(strWriter, XmlWriteMode.DiffGram);
info.AddValue("XmlDiffGram", strWriter.ToString());

The information stored in the SerializationInfo is then flushed to a binary stream or a Simple Object Access Protocol (SOAP) stream, according to the formatter in use.

The gist of this story is that a DataSet object is remoted using a couple of XML documents—one for the schema and one for the data—and the data is rendered using a DiffGram. To make DiffGrams really usable, the availability of schema information is vital.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.200.226