Microsoft & .NET .NET Building Persistence into .NET Programs

Building Persistence into .NET Programs

If you’ve read my article
Properly Building Persistent Programs
, you know the importance of
persisting selected information to enhance the user’s experience of your
applications. But perhaps you’re still a bit shakey on the mechanics. In
this article, I’ll work through a simple persistence example in C#, showing
how you can leverage the power of the .NET Framework to store information
safely and flexibly.

The Sample Application

Figure 1 shows a simple C# application that can benefit from a bit of
persistence. In this case, I want to save the form’s size and position, as well
as the data entered in the two textboxes. When the form is reloaded in the
future, it should use the saved information to initialize itself.

A form in need of persistence

In writing the code, I need to keep several key points in mind:

  1. The application should function normally even if the persisted information
    can’t be found. This will be the case the first time the application is
    run.
  2. The information should be persisted in a location that is available to all
    users, even if they are non-administrative or roaming users.
  3. The code should be simple and extensible, so that adding additional
    persisted information in the future is easy.

It All Starts With a Bit of Class

The first step in my solution is to define a class whose public fields will
store the required information:

using System;
namespace Persistence2
{
	/// <summary>
	/// Class to hold persisted settings
	/// </summary>
	[Serializable()]
	public class Settings
	{
		public int Top;
		public int Left;
		public int Height;
		public int Width;
		public string Name;
		public string Email;
	}
}

By storing all of the settings in a single class, I can vastly simplify the
code to read and writing settings (as you’ll see in a bit). The class also makes
for easy extensibility; I know that if I need to persist another bit of
information, I’ll just need to add one more public field to the class. At any
time, I’ll have at most one instance of this class in my application. In most
applications where I use this pattern, I instantiate the class from persisted
information when the application is launched, and create a new instance to save
when the application is being shut down.

Working Smarter, Not Harder

Now it’s time to consider the other requirements. I’ll start with saving the
settings when the application shuts down. The major problems to be dealt with
here are designing a format to hold the settings, and figuring out where to put
them. The format must be capable of holding all of the required information. The
location must be accessible to all users: administrators, non-administrators,
roaming users. Ideally the code should be the same on all supported operating
systems.

At this point, the novice programmer might sit down with the MSDN Library and
start slinging code, using API calls to retrieve the user’s data directory, and
writing low-level code to format the persisted information directly to a disk
file. But the experienced developer knows that there is a better way to proceed.
The .NET Framework includes hundreds of classes and thousands of members. If the
application can find the necessary functionality in the Framework, the developer
will have to write far less code. As a bonus, functionality that’s baked into
the Framework has probably had far more testing than your own code will ever
see.

Sure enough, there are Framework classes that do just about everything this
application needs. In particular, I’ll make use of the IsolatedStorageFile
class and the SoapFormatter class. Here’s my code for saving settings:

using System.IO;
using System.IO.IsolatedStorage;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Soap;

private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
	try
	{
		// Get the isolated store for this assembly
	    IsolatedStorageFile isf =
		    IsolatedStorageFile.GetUserStoreForAssembly();

		// Create or truncate the settings file
		// This will ensure that only the object we're
		// saving right now will be in the file
		IsolatedStorageFileStream isfs1 = 
		    new IsolatedStorageFileStream("HTMLBuilderSettings.xml",
		    FileMode.Create, FileAccess.Write, isf);

		// Create a settings object
		Settings s = new Settings();
		s.Top = this.Top;
		s.Left = this.Left;
		s.Height = this.Height;
		s.Width = this.Width;
		s.Name = txtName.Text;
		s.Email = txtEmail.Text;

		// Serialize the object to the file
		SoapFormatter SF = new SoapFormatter();
		SF.Serialize(isfs1, s);
		isfs1.Close();

	}
	catch (Exception ex)
	{
	// If settings can't be saved, next 
	// run will just the use form defaults
	}
}

The IsolatedStorageFile class is the .NET Framework’s answer to the problem
of where to store things on a user-by-user basis. Rather than force you to deal
with the different available locations in different circumstances, the Framework
takes care of all those messy details for you. When you instantiate an
IsolatedStorageFile object, you get back an entire virtual file system that you
can populate with directories and files. The user of the application is
guaranteed to be able to read and write to this virtual file system. If they
have a roaming profile, it will follow them around. In this particular bit of
code, I’ve asked the .NET Framework to deliver an isolated store based on both
user and assembly, so it won’t conflict with stores assigned to any other user
or application. After getting the store, I can create an
IsolatedStorageFileStream object in it, which acts like any other Stream
object.

The next step in the code is to create an instance of the Settings object and
to populate it with the appropriate data from the application. Finally, I want
to write the object out in a format that preserves its structure, and that can
be reconsistituted to the original object. Once again, the .NET Framework comes
through for me here. You’re probably familiar with the Simple Object Access
Protocol, SOAP, in the context of Web Services. SOAP specifies a way to encode
objects into XML. That’s exactly what I want to do here; fortunately, the
Framework lets me call directly into the SOAP formatting engine by instantiating
a SoapFormatter object and calling its Serialize method.

So, when this code runs, my Settings object will be packed up in a neat
little XML file and stored somewhere that the user can get at it. What about the
other end of the process?

Reconstituting the Settings Object

Because I want the persisted settings to be applied as soon as the form is
loaded, I’ve placed the code in the Form’s Load event handler:

private void Form1_Load(object sender, System.EventArgs e)
{
	try
	{
		// Get the isolated store for this assembly
		IsolatedStorageFile isf = 
			IsolatedStorageFile.GetUserStoreForAssembly();

		// Open the settings file
		IsolatedStorageFileStream isfs1 = 
			new IsolatedStorageFileStream("HTMLBuilderSettings.xml", 
			FileMode.Open, FileAccess.Read, isf);

		// Deserialize the XML to an object
		Settings s = new Settings();
		SoapFormatter SF= new SoapFormatter();
		s = (Settings) SF.Deserialize(isfs1);
		isfs1.Close();

		// And apply the settings to the form
		this.Top = s.Top;
		this.Left = s.Left;
		this.Height = s.Height;
		this.Width = s.Width;
		txtName.Text=s.Name;
		txtEmail.Text=s.Email;

	}
	catch (Exception ex)
	{
		// No file found. Use the form's default settings
	}

}

A little inspection should convince you that the loading code is just the
reverse of the saving code. Open the settings file, deserialize the XML to an
object, and then pick apart the object’s public fields to get at the persisted
information. If anything goes wrong, the form will just open with its default
settings (the size and contents that were saved in the designer when the
application was compiled.

Code You Can Use

That’s all there is to it! To use this code in your own applications, you
just need to alter the Settings object so that it has public fields for each
piece of information that you’d like to persist. Then read from those fields
when the application starts, and write to them when it ends. Your users will
thank you for the extra effort.

About the Author

Mike Gunderloy is the author of over 20 books and numerous articles on
development topics, and the lead developer for Larkware. Check out his MCAD 70-305, MCAD
70-306, and MCAD 70-310 Training Guides from Que Publishing. When he’s not
writing code, Mike putters in the garden on his farm in eastern Washington
state.

Latest Posts

Related Stories