Arian Kulp's Blog
opinion, insight, and occasional code

Loading and Saving Datasets

Wednesday, April 12, 2006 6:39 AM
Well, I decided a few months ago that this blog would be my vehicle for disseminating tips and tricks that I came across during my daily coding, in addition to my essays, rants, and opinions. Based on my frequency of posts, apparently I don't have much to say! I actually keep a running list of things I'd like to cover, but honestly priority is a bit low compared to my paid work!

Today, I've got a pretty bried entry to share some great information on saving datasets to disk. This comes up a lot in my Coding 4 Fun applications, and some of my home/personal ones too. DataSets are just so darned handy for managing relational data without the overhead of a database. It's so much portable when you can just copy the EXE and XML file to a new machine. Of course, detached database files with SQL Server 2005 are almost as handy, but the SQL Server engine must be already installed. Anyway!

The below code snippets are a nicer version of LoadEntries/SaveEntries as seen in my Task Manager article. This is not a huge amount of code, but it turns out to be more than is necessary. LoadEntries currently creates the XmlSerializer object, then a StreamReader, then explicitly deserializes the object, finally cleaning everything up:

/// <summary>
/// Loads the entries using XML deserialization
/// </summary>
public void LoadEntries()
{
try
{
XmlSerializer s = new XmlSerializer(typeof(TasksDataSet));
TextReader r = new StreamReader("TaskList.xml");
ds = (TasksDataSet)s.Deserialize(r);
r.Close();
}
catch
{
ds = new TasksDataSet();
}

foreach (TasksDataSet.TasksRow row in ds.Tasks)
{
UpdateDisplayWithRow(-1, row);
}
}

It turns out that we can simplify the entire try block down considerably. If you don't care about error-checking (not the best idea...) you could implement a LoadEntries method in a single line of code:

ds.ReadXml("TaskList.xml", XmlReadMode.IgnoreSchema);

Yep. It turns out that the DataSet in .NET 2.0 has built-in save-to-file functionality! Specifiying IgnoreSchema reduces the file size by only saving data. It turns out that this is very helpful when you are using typed datasets too, since if you add a new column, then try to deserialize an existing file, having that embedded schema causes compatibility problems. The SaveEntries method can see similar reductions in size. Currently, we use the same basic serializer/stream/serialize way of saving:

/// <summary>
/// Saves the entries using XML serialization
/// </summary>
public void SaveEntries()
{
try
{
XmlSerializer s = new XmlSerializer(typeof(TasksDataSet));
TextWriter w = new StreamWriter("TaskList.xml");
s.Serialize(w, ds);
w.Close();
}
catch( Exception x )
{
appNotifyIcon.ShowBalloonTip(8000,
"Coding 4 Fun - Task Manager",
"Tasks could not be saved.\n" + x.Message, ToolTipIcon.Error);
}
}


Replace the code in the try block with:
ds.WriteXml("TaskList.xml", XmlWriteMode.IgnoreSchema);

Voila! So easy. I suggest leaving in the try/catch and even keeping the calls in their own methods for the best isolation and reuse. Errors could occur on load if no file exists. Save errors would occur if the file was locked. Finally, you may want to add additional logic to execute whenever saves and loads occur such as merging unsaved changes, or populating UI controls (as seen in the foreach block in LoadEntries).

I hope this helps someone. It doesn't really add any functionality (except possibly compatibility without the schema), but it's short and easy to remember. A single method built into the DataSet object does some nice work for us!
Comments have been closed on this topic.