Frictionless data persistence in Silverlight 2
While asking the user explicitly for an increased quota makes good sense, it should be avoided whenever possible because it is irrelevant to the experience of using the application and it represents a potential barrier to use. With that in mind, the scant 100 kilobytes we have to work with could be stretched significantly by providing the GZipStream and DeflateStream classes from System.IO.Compression. Unfortunately that is not the case.
Two things get in the way in Silverlight when we attempt to serialize or compress values: serialization, and compression. You can find an approach to serialization here, and a port of ICSharpCode’s SharpZipLib here.
The goal of this persistence solution is to facilitate the storage and retrieval of user state in Silverlight 2, making it easy for the developer to work with plain objects, and then declare where, how, and when state is persisted by the solution. This is the same pattern I applied to WebForms earlier.
Currently the number of targets (where to put the state data) is only two, and there are no additional options (special rules for the content prior to storing it in the target), though we could add encryption in the future. In this solution we have the option of storing state in IsolatedStorage, or CompressedIsolatedStorage; the former is the natural place for user storage, and the latter zips the file to greatly increase the amount of state you can keep on the client before the user is prompted to increase his or her quota (especially if you’re using strings).
[PersistentValue("UncompressedTest", "Uncompressed.dat", PersistentValueTargets.IsolatedStorage)]
public string UncompressedTest { get; set; }
[PersistentValue("CompressedTest", "Compressed.dat", PersistentValueTargets.CompressedIsolatedStorage)]
public string CompressedTest { get; set; }
These attributes allow you to declare the property’s key, the storage file name you wish to use (in order to provide separation between compressed and uncompressed stores, to name one purpose), and where your state is stored while you use the plain object. You don’t need to worry about much else, other than calling the methods that save and load state from isolated storage. Remember that Silverlight state persists on the user’s physical drive, so state may be persisted indefinitely.
public Page()
{
InitializeComponent();
// Reset state (comment this out to see state persist across visits)
this.ResetIsolatedStorage(UncompressedFilename);
this.ResetIsolatedStorage(CompressedFilename);
// Load initial state
this.LoadPersistentValues();
// UI code elided
}
private void btnChangeValues_Click(object sender, RoutedEventArgs e)
{
// Increment test fields and persist them
for (var i = 0; i < 500; i++)
{
UncompressedTest += "ORLY?";
CompressedTest += "ORLY?";
}
// Save values
this.SavePersistentValues();
// Sanity check; mess with the test fields
UncompressedTest = CompressedTest = "FAIL.";
// Load persisted fields and display new values
this.LoadPersistentValues();
// UI code elided
}
Special considerations
The types of classes declared by you must match the types intrinsically supported by the DataContractJsonSerializer; if you want to pass around custom types to your utility storage class, you need to either persist its state as strings that you know how to re-assemble, or implement a custom serialization framework to ensure the values are created properly. As an example, if you try to use this framework to persist a List<string> collection, you will receive an error when you attempt to load your isolated storage for the first time, since the serializer will have no way of knowing how to convert an object[] instance into a List<string> instance. The headache of providing a custom implementation as we’ve seen makes it worth keeping your client state simple, and rehydrating more complex types by hand.
I hope you find this declarative persistence pattern useful!
Download: VS 2008







[...] a goal to create a Silverlight declarative persistence solution similar to one I recently wrote for ASP.NET WebForms, I quickly discovered that [...]
[...] last little while I’ve been working on a persistence solution for Silverlight that requires both serialization and compression to have any value. These two concepts are, [...]