Cover

Architecting WP7 - Part 4 of 10: Client-side Data

October 14, 2010
No Comments.

In this fourth part of my Architecting Windows Phone 7 series, I will tackle client-side data. If you’ve missed the past parts of the series, you you can visit them here:

One of the main complaints about Silverlight on the phone (especially to WinMo veterans) is the lack of a local database store. But being able to store data is still really possible, it is just that any data you store locally is not necessarily in a database format.  But maybe that doesn’t matter as much as you might expect.

While not having an on-disk relational store is a limitation, depending on the size of your data, you can do quite a lot without one. In my “Stay Glucose to Me” application, I decided to just store the data on disk. In a 1.1 version, I am going to support a multi-file approach so that it only keeps the last 3 months in memory, but for now it just stores it all as a single file. This file is married to my Model like so:

public class GlucoseModel
{
  private const string READINGSFILE = "readings.xml";

  // ...
  
  User ReadUser()
  {
    User user = null;

    using (var store = 
      IsolatedStorageFile.GetUserStoreForApplication())
    {
      if (store.FileExists(READINGSFILE))
      {
        using (var file = store.OpenFile(READINGSFILE, FileMode.Open))
        {
          var writer = new XmlSerializer(typeof(User));
          var result = writer.Deserialize(file);
          user = result as User;
        }
      }

      if (user == null)
      {
        user = new User();
      }
    }

    return user;
  }

  public void SaveUser(User user)
  {
    using (var store = 
      IsolatedStorageFile.GetUserStoreForApplication())
    using (var file = store.CreateFile(READINGSFILE))
    {
      var writer = new XmlSerializer(typeof(User));
      writer.Serialize(file, user);
      file.Flush();
      file.Close();
    }
  }

}

I am using simple XML Serialization to store the data. I thought about doing JSON instead for size on disk, but ran into some bugs so switched to XML Serialization. Note that I am using Isolated Storage for the data. In my case, I am saving the data as it changes so that I never have to worry about tombstoning. This does imply that I am loading all the data on load.  Long-term this won’t work in my case, but depending on your app, this isn’t hard to fix with multiple files.

Two issues come up with this strategy: serialization and querying. I am mixing my data objects with some temporary data (StartDate and EndDate). These properties are used to determine what data to show and shouldn’t be serialized (or in some cases can’t be serialized).

public class User : ObservableObject // Implements INPC for me
{
  public User()
  {
    Readings = new ObservableCollection();
  }

  string _name = "";
  public string Name
  {
    get { return _name; }
    set
    {
      _name = value;
      RaisePropertyChanged("Name");
    }
  }

  // ...

  ObservableCollection _readings;
  public ObservableCollection Readings
  {
    get { return _readings; }
    private set
    {
      _readings = value;
      RaisePropertyChanged("Readings");
    }
  }

  [XmlIgnore]
  public DateTime StartDate
  {
    get { return DateTime.Now.AddDays(-21); }
  }

  [XmlIgnore]
  public DateTime EndDate
  {
    get { return DateTime.Now; }
  }

}

In my case I wanted to omit them from my serialization so using the XmlIgnore attribute did the trick. So controlling serialization is something you may want to implement (depending on your data of course).

The second issue is querying. This is a lot trickier. With my initial approach of load *all* the data, I can just use LINQ:

var qry = from r in _theUser.Readings
          where r.ReadingDateTime >= App.ViewModel.CurrentUser.StartDate
          group r by r.ReadingDateTime.Date into dayGroup
          select new ReadingDay
          {
            Date = dayGroup.Key,
            Value = dayGroup.Average(r => r.Value)
          };

But constructing these sorts of queries without loading all the data is a definite problem with client-side data. I don’t have an answer.  But if you are just using the client-side data as a cache, complex queries could be done at the server (e.g. WCF, OData, etc.).

The important part of this story is that you can accomplish a lot without resorting to a client-side database, but there are limitations. I know there are a few WP7 compatible in-memory databases but I haven’t looked at them or their memory footprint yet. With the size considerations of data in WP7, I don’t see how most projects would need this. It will be awesome when we get it, but you can certainly work around it.