DataSet Serialization

for RuBoard

DataSet Serialization

On Day 1, you were introduced to the concept of the DataSet by learning that it's the core of the disconnected programming model in ADO.NET. Because it's useful in disconnected scenarios and distributed applications, it must be able to be serialized and deserialized when passed between tiers in an application or persisted to disk. In this final section for today, you'll learn how the DataSet is passed between tiers in a distributed application and how you can persist it to disk in a disconnected applications.

Passing DataSet s

By default, an instance of a class in .NET can be passed by value or by reference to, as well as returned from, any method exposed by an object that lives in the same Application Domain. However, in distributed applications, the presentation and data access code may reside on different servers and thus different AppDomains.

What's an AppDomain?

graphics/newterm.gif

Although not discussed on Day 1, Application Domains (AppDomains) are partitions inside a process set up by the common language runtime to provide isolation and fault tolerance. You can think of AppDomains as lightweight processes controlled by the common language runtime that offer better performance than using a technique such as process spawning prevalent in the Unix world. Typically, a client application will have a single AppDomain created by the common language runtime when the application loads. However, you can programmatically create additional AppDomains through the System.AppDomain class. Server applications such as ASP.NET, on the other hand, can and do create multiple AppDomains to service multiple requests or clients and maintain isolation between them.

graphics/newterm.gif

In order for an object to be referenced or copied across AppDomains, it must be able to be, in .NET parlance, remotable. A remotable type is one that derives from System.MarshalByRefObject or System.MarshalByValueComponent , implements the ISerializable interface, or is marked with the Serializable attribute. Deriving from MarshalByRefObject allows an object to be simply referenced from another AppDomain, but it doesn't allow it to be copied. The other three options in the previous list all allow the object to be serialized. Because the DataSet needs to be copied, it's derived from MarshalByValueComponent , implements the ISerializable interface, and is marked with the Serializable attribute. This allows a DataSet created in one AppDomain, perhaps residing in a separate process or on a separate machine, to be passed to a different AppDomain.

Although the presence of the Serializable attribute alone allows a .NET type to be serialized to XML automatically using a generic format, you'll notice that the DataSet implements ISerializable in order to implement a custom algorithm to perform the serialization. This is the case so that the DataSet can fully represent changes made to its data through the DiffGram grammar discussed yesterday . You can get a glimpse of some of the work the DataSet does by examining the code in the TitlesDs class. You'll notice that much of it is devoted to overriding methods of the base class that have to do with serialization. The ShouldSerializeTables , ShouldSerializeRelations , GetSchemaSerializable , ReadXmlSerializable , and especially the protected constructor that accepts an object of type SerializationInfo all work to ensure that the DataSet can be serialized to and from XML. Equivalent code exists in the base DataSet class and so is invisible to developers when working with generic DataSet objects.

graphics/newterm.gif

Once a type is able to be serialized, it can then be copied between AppDomains using .NET Remoting. Briefly, .NET Remoting is the set of .NET Framework components that allow two pieces of managed code (a client and a server) running in separate AppDomains to communicate. The client and the server communicate via an HTTP or TCP channel that is specified in application configuration files or configured directly in code within the application. Remoting allows a client to instantiate a class that lives in the server process (a MarshalByRefObject ) in order to call its methods. A method in the server class can then create a DataSet (a MarshalByValueComponent ) and pass a copy of it back to the client. The object, such as a DataSet , is then serialized for transport either in an XML format wrapped in a SOAP message or in a compact binary format. Which formatter is used can also be configured in the application configuration files. When the serialized DataSet reaches the client, it's then deserialized into an actual DataSet object. The end result is that a copy of the DataSet now exists on the client and can be manipulated just as any other DataSet . A diagram of this process can be seen in Figure 6.3.

Figure 6.3. NET Remoting. This diagram depicts a DataSet as it is remoted using .NET Remoting.

graphics/06fig03.gif

Note

For an overview of .NET Remoting, see Chapter 8 of my book Building Distributed Applications with Visual Basic .NET , published by Sams.


A typical scenario for this process includes a managed data access class hosted in a Server Application in Component Services. In this case, the class resides in a separate AppDomain because it's running in a separate process (AppDomains don't cross operating system processes). When the client calls a method of the data access class such as GetTitles , the method communicates with the database to retrieve the results and populate a DataSet . The DataSet is then passed back to the client using .NET Remoting as described earlier.

Note

Another example of where serialization is used is in returning a DataSet from an XML Web Service, as you'll learn on Day 18, "ADO.NET and XML Web Services."


Persisting to Disk

In addition to the scenario where a DataSet is passed between tiers of an application, a common use for serialization is in a truly disconnected application. In these types of applications, the client might need to work with the DataSet offline and then only later reconnect to the network and synchronize its changes with a data store.

Fortunately, the ability of the DataSet to store and track its changes makes it ideal for this type of application. Coupled with the support of classes in the .NET Framework, persisting a DataSet to disk is almost trivial.

To illustrate how this can be done, consider the code in Listing 6.5 that shows methods to save and load a DataSet from the file system.

Listing 6.5 Saving and loading a DataSet . These methods use the XmlSerializer class to save and load a DataSet using XML.
 public Boolean SaveDs(DataSet ds, string fileName) {   // Open the file for reading   FileStream fs;   try   {     fs = new FileStream(fileName,FileMode.Create,       FileAccess.Write,FileShare.Write);    }    catch (IOException e)    {      throw new Exception("Cannot open  " + fileName , e);    }    // Serialize to the file    try    {      XmlSerializer ser = new XmlSerializer(ds.GetType());      ser.Serialize(fs,ds);    }    catch (Exception e)    {      throw new Exception("Cannot save " + ds.DataSetName, e);     }     finally     {       fs.Close();    }    return true; } public DataSet LoadDs(String fileName, Type dsType) {    FileStream fs;    XmlReader xr;    // Make sure the file exists    if (File.Exists(fileName))    {      fs = new FileStream(fileName,FileMode.Open,        FileAccess.Read,FileShare.Read);      xr = new XmlTextReader(fs);     }     else throw new Exception(fileName + " does not exist");    // Deserialize    XmlSerializer ser = new XmlSerializer(dsType);    DataSet ds = new DataSet();    try    {      if (ser.CanDeserialize(xr))      {        ds = (DataSet)ser.Deserialize(xr);       }    }    catch (Exception e)    {       throw new Exception("Could not load " + fileName,e);    }    return ds; } 
graphics/analysis.gif

Listing 6.5 contains two methods, SaveDs and LoadDs , which use the XmlSerializer class from the System.Xml.Serialization namespace. The XmlSerializer class can be used with any class that includes the Serializable attribute. As you can see from SaveDs , the DataSet to serialize and the file name and path are passed to the method. The method then opens the file for reading and instantiates the XmlSerializer , passing it the type to serialize. If all goes well, the Serialize method can then be called, which serializes the DataSet to the stream passed as the first argument. If you inspect the books.xml file, you'll notice that it contains both the XSD and the complete DiffGram for the DataSet .

You can also see from Listing 6.5 that the SaveDs and LoadDs methods accept and return the generic DataSet class, and so can also be used with any derived DataSet class through polymorphism.

Note

As you'll see tomorrow, this functionality overlaps the functionality of the WriteXml , WriteXmlSchema , InferXmlSchema , ReadXml , and ReadXmlSchema methods of the DataSet class.


The LoadDs method then does the opposite by first making sure that the passed-in file exists and then opening the file for reading. This method also accepts an argument that specifies the type to deserialize to. This is necessary so that you can pass strongly typed DataSet objects into the method and be able to cast them back to their strong type once the method has completed. Because the file will be an XML file, it's passed to the constructor of an XmlTextReader . This is done so that the CanDeserialize can be called to determine whether the document will be able to be deserialized. If so, the Deserialize method is called returning a DataSet object.

The client code to use these methods might look something like the following.

 TitlesDs books = new DataSet(); // Fill the DataSet books.ExtendedProperties.Add("SaveTime",DateTime.Now); SaveDs(books,"books.xml"); // Now shut down the app and return later DataSet newBooks = new DataSet(); String strTime; newBooks = (TitlesDs)LoadDs("books.xml",newBooks.GetType()); strTime = "DataSet was saved at " +   books.ExtendedProperties["SaveTime"].ToString(); 

You'll notice from this snippet that the ExtendedProperties collection was used to store the time the DataSet was saved to disk and can then be read once the DataSet is deserialized.

for RuBoard


Sams Teach Yourself Ado. Net in 21 Days
Sams Teach Yourself ADO.NET in 21 Days
ISBN: 0672323869
EAN: 2147483647
Year: 2002
Pages: 158
Authors: Dan Fox

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net