Persisting User Settings in Silverlight

One topic that comes up frequently in dev circles is persisting data in Silverlight applications.  There are a number of ways to do this, and the right solution depends on the data that is being stored.  In ASP.NET applications, user settings are typically stored in a database and often abstracted through a mechanism like the ASP.NET Profile provider in conjunction with the ASP.NET Membership provider.  The make the end user experience a bit better, log in state (or simply the username) is persisted in a cookie. 

In Silverlight, this is still a viable approach (although the data is typically exposed via webservices, depending on the application).

Another approach, however, is to use isolated storage.  Isolated storage can be an effective tool for caching data (with a number of caveats).  In the example below, I’ve created a very simple class that contains the application settings I’d like to persist.   The key methods are Load() and Save().   Out of the box, this will work and you can simply add/remove properties as you’d like. 

   1: public class ApplicationSettings
   2:     {
   3:         [DefaultValue(6)]
   4:         public int MinZoom { get; set; }
   5:  
   6:         [DefaultValue(13)]
   7:         public int MaxZoom { get; set; }
   8:  
   9:         [DefaultValue(8)]
  10:         public int PlotDelay { get; set; }
  11:  
  12:         [DefaultValue(false)]
  13:         public bool RememberSettings { get; set; }
  14:  
  15:         public ApplicationSettings() { }
  16:  
  17:         public static WorldmapsSettings Load()
  18:         {
  19:             ApplicationSettings settings = new ApplicationSettings();
  20:  
  21:             using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
  22:             {
  23:                 if (!store.FileExists(@"ApplicationSettings.xml"))
  24:                 {
  25:                     return settings;
  26:                 }
  27:  
  28:                 using (var isoStream = store.OpenFile(@"ApplicationSettings.xml",
  29:                     FileMode.Open))
  30:                 {
  31:                     XmlSerializer s = new XmlSerializer(typeof(ApplicationSettings));
  32:                     TextReader r = new StreamReader(isoStream);
  33:                     settings = (ApplicationSettings)s.Deserialize(r);
  34:                     r.Close();
  35:  
  36:                     if (settings != null && settings.RememberSettings)
  37:                     {
  38:                         return settings;
  39:                     }
  40:                     else
  41:                     {
  42:                         return new ApplicationSettings();
  43:                     }
  44:                 }
  45:             }
  46:         }
  47:  
  48:         public void Save()
  49:         { 
  50:             using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
  51:             {
  52:                 using (IsolatedStorageFileStream isoStream = store.OpenFile(@"ApplicationSettings.xml",
  53:                     FileMode.Create))
  54:                 {
  55:                     XmlSerializer s = new XmlSerializer(typeof(ApplicationSettings));
  56:                     TextWriter writer = new StreamWriter(isoStream);
  57:                     s.Serialize(writer, this);
  58:                     writer.Close();              
  59:                 }
  60:             }
  61:         }
  62:     }

[EDIT]

Silverlight guru Tim Heuer pointed out I’m doing a lot of extra work I don’t need to.   The ApplicationSettings of the IsolatedStorageSettings allows us to stuff objects in it pretty cleanly – so the above could be implemented like so:

   1: public static WorldmapsSettings Load()
   2: {
   3:     
   4:     WorldmapsSettings settings = null;
   5:     
   6:     if (IsolatedStorageSettings.ApplicationSettings.Contains("foo"))
   7:     {
   8:         settings = IsolatedStorageSettings.ApplicationSettings["foo"] as WorldmapsSettings;
   9:     }
  10:     
  11:     if (settings == null)
  12:     {
  13:         settings = new WorldmapsSettings();
  14:     }
  15:  
  16:     return settings;  
  17: }
  18:  
  19: public void Save()
  20: {
  21:     IsolatedStorageSettings.ApplicationSettings["foo"] = this;
  22: }

I can’t think of a good reason not to do it this way, unless some more complex serialization is called for, but even then I can’t come up with a good scenario for that. 

[/EDIT]

 

Now for the caveat.  Isolated storage isn’t secure unless you take some measures to secure it manually.  While you could encrypt the contents, I’d probably recommend not storing data on the client if you’re saving sensitive data.  In the above example, I’m serializing the data using the XML serializer, so the data is obviously in plain text and stored locally:

image

… and it contains the expected object:

   1: <ApplicationSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   2:  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   3:  <MinZoom>2</MinZoom> 
   4:  <MaxZoom>16</MaxZoom> 
   5:  <PlotDelaySeconds>14</PlotDelaySeconds> 
   6:  <RememberSettings>false</RememberSettings> 
   7: </ApplicationSettings>

Isolated storage offers a lot of potential, but it’s also important to remember the security implications in both exposing the data as well as potential injection points.

Comments (3) -

timheuer
timheuer
4/13/2009 9:31:07 AM #

Curious your thoughts on the benefit of this approach versus the built-in methods for IsolatedStorage like: IsolatedStorageSettings.ApplicationSettings["foo"] = "somevalue";

bhitney
bhitney
4/13/2009 9:57:46 AM #

Hey Tim -- wow, you know I didn't try that but I just gave it a whirl.  To be honest I don't know why I didn't consider it -- perhaps legacy "DIY" assuming I couldn't stuff an object reference in there.  

I updated the post above with the built in ApplicationSettings implementation.  I can't think a solid reason to not use ApplicationSettings except to possibly control the serialization process with more complex types, but that's clearly not needed here... what do you think?

timheuer
timheuer
4/14/2009 12:55:42 PM #

I think for simple stuff like you demonstrate, using the built-in mechanism seems to make sense.  But you are right if you wanted to save a "Order" object, it might make sense to serialize it and save it.

Pingbacks and trackbacks (1)+

Comments are closed

My Apps

Dark Skies Astrophotography Journal Vol 1 Explore The Moon
Mars Explorer Moons of Jupiter Messier Object Explorer
Brew Finder Earthquake Explorer Venus Explorer  

My Worldmap

Month List