SQL database version management in a Windows Phone 7 application

This is the second tips’n’tricks post talking about the stuff I discover while working on the 2Day todo-list application for Windows Phone. In my last blog post I talked about random ArgumentException. This time, I’m talking about the way to handle version management in SQL database in a Windows Phone 7 app.

As soon as you want to push an update to an existing app using a SQL database you will have to deal with version management. The basic idea is that you might want to alter the schema of the database (for examples by adding tables or columns) while not breaking compatibility for your existing users. You must therefore be able to upgrade your database from version N to version N + 1.

On the Windows Phone platform, this is done using the DatabaseSchemaUpdater class. You can get an instance of this type using the CreateDatabaseSchemaUpdater() method of your DatabaseContext. You can then change the database by using the AddTable and AddColumn methods. You can retrieve the version of the database by using the DatabaseSchemaVersion property.  Simple, isn’t it ?

Here are some tips to update your database properly:

  • define all your update operations in a single class – it will be easier for you to manage future updates
  • define the current version of your database using a constant in your class – check this constant agains the DatabaseSchemaVersion property to determine if you must perform an upgrade
  • handle incremental update, when you push an update for version 3, you can still have users with version 1 or 2, so handle upgrade from 1 to 2 and from 2 to 3 incrementally
  • when you add new column to an existing table, the type of this colum must be nullable (bool?, string?, etc.)

Here is what the code looks like in 2Day:

using Japf.ToDo.Core.Model;
using Microsoft.Phone.Data.Linq;

namespace Japf.ToDo.Core.IO
{
    public class DatabaseVersion
    {
        // database history
        // v0. Feb. 20th 2011 first version
        // v1. May. 7th 2012 add the SpecialFolders table
        // v2. May. 19th 2012 add the Progress column in the Task table
        // v3. Auguest. 12th 2012 add the IsFocused column in the Folders & SpecialFolders table
        public const int Version = 3;

        public static void UpgradeIfNeeded(DatabaseContext context)
        {
            DatabaseSchemaUpdater dbUpdater = context.CreateDatabaseSchemaUpdater();
            if (dbUpdater.DatabaseSchemaVersion < Version)
            {
                // handle update for user with version 0
                if (dbUpdater.DatabaseSchemaVersion == 0)
                {
                    From0To1(context);
                    From1To2(context);
                    From2To3(context);
                }
                // handle update for user with version 1
                else if (dbUpdater.DatabaseSchemaVersion == 1)
                {
                    From1To2(context);
                    From2To3(context);
                }
                // handle update for user with version 2
                else if (dbUpdater.DatabaseSchemaVersion == 2)
                {
                    From2To3(context);
                }
            }
        }

        private static void From0To1(DatabaseContext context)
        {
            var dbUpdater = context.CreateDatabaseSchemaUpdater();

            // add a new table containing where each row is a SpecialFolder
            dbUpdater.AddTable();

            // add a column to the existing table Folder named ShowInSpecialFolders
            dbUpdater.AddColumn("ShowInSpecialFolders");

            // update the version of the schema
            dbUpdater.DatabaseSchemaVersion = 1;

            // send the changes to the db
            dbUpdater.Execute();

            // do not reuse the DatabaseSchemaUpdater for other update as it still contains
            // instructions to add the new table and the new column
        }

        // other methods omitted for clarity...
    }
}

Hope it helps 🙂

Random ArgumentException in a Windows Phone 7 application

While working on the last update of the 2Day todo-list application for Windows Phone, I encountered a strange bug I wanted to share in this post. The symptoms were very awkward: an ArgumentException where thrown randomly while using the app:

As always when I got that kind of stuff, I try to find out a reproducible set of actions which lead to the crash. Unfortunately in this case it wasn’t that easy.

After a couple of try, I found out I could reproduce the problem by doing the same set of actions many times:

  • launch 2Day
  • navigate to the details view of a task
  • go back to the home screen
  • navigate to the details view of another task
The crash then happened between 1 and 20 tries. The stacktrace wasn’t very helpful:
at MS.Internal.XcpImports.CheckHResult(UInt32 hr)
at MS.Internal.XcpImports.Collection_AddValue[T](PresentationFrameworkCollection`1 collection, CValue value)
at MS.Internal.XcpImports.Collection_AddDependencyObject[T](PresentationFrameworkCollection`1 collection, DependencyObject value)
at System.Windows.PresentationFrameworkCollection`1.AddDependencyObject(DependencyObject value)
at System.Windows.Controls.UIElementCollection.AddInternal(UIElement value)
at System.Windows.PresentationFrameworkCollection`1.Add(UIElement value)
at System.Windows.Controls.ItemsControl.AddVisualChild(Int32 index, DependencyObject container, Boolean needPrepareContainer)
at System.Windows.Controls.ItemsControl.AddContainers()
at System.Windows.Controls.ItemsControl.RecreateVisualChildren(IntPtr unmanagedObj)
at MS.Internal.XcpImports.MeasureOverrideNative(IntPtr element, Single inWidth, Single inHeight, Single& outWidth, Single& outHeight)
at MS.Internal.XcpImports.FrameworkElement_MeasureOverride(FrameworkElement element, Size availableSize)
at System.Windows.FrameworkElement.MeasureOverride(Size availableSize)
at Microsoft.Phone.Controls.Pivot.MeasureOverride(Size availableSize)
at System.Windows.FrameworkElement.MeasureOverride(IntPtr nativeTarget, Double inWidth, Double inHeight, Double& outWidth, Double& outHeight)
After some time spend on the Internet, I found out this post on StackOverflow. It basically told me something could be wrong with the naming of the elements in the XAML. Actually I did change some XAML in 2Day in order to change the layout of the task create/edit page:
During that refactoring I changed the way the notes are displayed and I created a UserControl for that purpose. Ultimately, I found out that this control was named: it has a x:Name set at the top of the XAML (note the line 8 in the following sample):

I needed that because I wanted to databind the Text property of a TextBox on a Dependency Property defined on the control itself:

Because the post on StackOverflow talked about naming controls, I thought that could be the cause of the error. It it was actually ! Removing this single piece of XAML (and changing the binding a little bit) removed the crash from the app !

This is definitively the strangest bug I’ve seen on the Windows Phone platform… Hope it helps 🙂

If you want to give a try to a fast & fluid todo-list app for Windows Phone 7, feel free to download 2Day:

MVP 2012-2013 !

Yesterday, Sunday July 1st I received a great news from Microsoft: I have been given the MVP award for the third consecutive year 🙂

I’m very proud to be part of what I called a “family”. The last year has been very busy for me (with the trip to //BUILD/ for example) and even though I wasn’t very active in the last few weeks I still have many things to share soon ! I also strongly hope to be able to go back to Redmond next February for the MVP Summit…

I would like to send a particular thank to my colleague Charlotte who was speaking with me in the last French TechDays and also helped me to write an article in a magazine…

Thanks everyone,

Jeremy