WP7 - Backup Service

Print Content | More

I hate when I loose my application data and there are times when developing a Windows Phone 7 Application that your IsolatedStorage data gets simply discarded when deploy an application from Visual Studio.

This usually happens when you perform a solution cleanup or a complete rebuild; when you deploy you application again on the phone all your previously saved data are simply wiped out. This happens because under these circumstances Visual Studio performs a complete uninstall of your application before installing the new version.

In order to prevent (better to say...to limit) this scenario and to give the applications the ability to backup/restore their data I’ve created a solution project that you can use to create dump images of all the IsolatedStorage data that are tied with your application.

The concept idea is nothing new really: have a WCF service you can use to stream your files back and forth between your WP7 and PC. It is actually the easiest way you can solve this problem; it has been suggested in various resources along the internet but I wasn’t able to find a complete working solution, so I took the weekend and put on my own project on the road.

Let’s start with some project requirements:

  • The backup/restore service must be generic: that is it must be suitable for any kind of applications and not built for a specific scenario in mind.
  • The backup service should be able to handle more than a single application; we will identify the different application with an Application string tag.
  • It should support different backup set for the same application (ideally you would be able to create multiple backup/restore points), each backup set will be identified by a given name.
  • It should preserve the folders and file structure your application have created on the IsolatedStorage (we just want to dump all the files, so we can inspect and edit them if needed before restoring the state).
  • The WCF service should be self hosted inside a WPF application, I do not want to force people to configure it in IIS, they just have to launch an exe on their PC (using administrative rights).

What I wasn’t worried about in this version:

  • The backup/restore operation isn’t protected against ‘tombstoning’, if your process gets interrupted in the middle you will likely have to start it over again. It is a SAFE procedure anyway, we are reading all the files while backing up, nothing gets (or should) altered at this stage. And if the process is interrupted during the restore stage, just start it all over again (you have a working copy of your data on the disk anyway).
  • Lack of progress notification, you will just get an event raised when the operations are completed.

I will not discuss every aspect of the project, because it isn’t really complex (and you will also have the full source code to check and use, just mention me if you do  Smile), so without further ado here is the solution structure:

  • WP7BackupService - this project holds the WPF application that hosts the WCF service, it defines the basic interface and classes you use to interact with the service.
  • WP7BackupServicePhone - this is the WP7 project holds reference to the WCF service and exposes the helper class you will use to initialize the backup sets and perform the backup and restore operations.

WP7BackupService

This project is quite straightforward, so I won’t dig into it too much, I’ll just report some of the interfaces and classes that are used to define the WCF service and its interactions with the outside world.

/// <summary>
/// A class that defines the file to upload
/// </summary>
[DataContract]
public class FileUploadInfo
{
	/// <summary>
	/// the specific application for which we are storing the file
	/// </summary>
	[DataMember]
	public string Application;

	/// <summary>
	/// The tag that identifies the backup set
	/// </summary>
	[DataMember]
	public string Set;

	/// <summary>
	/// The name of the file to mirror on the  server
	/// this will contain the full path as you have it inside the 
	/// IsolatedStorage
	/// </summary>
	[DataMember]
	public string Name;

	/// <summary>
	/// The content bytes to send
	/// </summary>
	[DataMember]
	public byte[] Bytes;
}

/// <summary>
/// A class to specify which file to download
/// </summary>
[DataContract]
public class FileDownloadInfo
{
	/// <summary>
	/// The specific application for which we are retrieving the file
	/// </summary>
	[DataMember]
	public string Application;

	/// <summary>
	/// The tag that identifies the backup set
	/// </summary>
	[DataMember]
	public string Set;

	/// <summary>
	/// The name of the file to mirror on the device
	/// this will contain the full path as you'll have it inside the 
	/// IsolatedStorage
	/// </summary>
	[DataMember]
	public string Name;
}

/// <summary>
/// It carries the informations in a backup set, that is all the directoryes and files
/// to keep it simple all the path are relative to the set root
/// and both the lists are plain lists
/// </summary>
[DataContract]
public class BackupSet
{
	[DataMember]
	public string[] Folders;

	[DataMember]
	public string[] Files;
}

 

/// <summary>
/// Defines the Backup Service contract interface
/// </summary>
[ServiceContract]
public interface IBackupService
{
	/// <summary>
	/// Initializes the backup set, creates the directory structure if it does not exists,
	/// wipe out any existing folder if it has the same path.
	/// </summary>
	/// <param name="application">The application.</param>
	/// <param name="backupSet">The backup set.</param>
	[OperationContract]
	void InitBackupSet(string application, string backupSet);

	/// <summary>
	/// Gets the backup set info.
	/// </summary>
	/// <param name="application">The application.</param>
	/// <param name="backupSet">The backup set.</param>
	/// <returns>A class containing the Folders and Files we can request along with their full path that
	/// will be used inside the IsolatedStorage</returns>
	[OperationContract]
	BackupSet GetBackupSetInfo(string application, string backupSet);

	/// <summary>
	/// Uploads the file as a byte array.
	/// </summary>
	/// <param name="fileUploadInfoInfo">Application,BackupSet,Filename and content bytes</param>
	/// <returns></returns>
	[OperationContract]
	void UploadBytes(FileUploadInfo fileUploadInfoInfo);

	/// <summary>
	/// Opens a stream for downloading data.
	/// </summary>
	/// <param name="fileDownloadInfoInfo">The file download info.</param>
	/// <returns></returns>
	[OperationContract]
   	Stream DownloadStream(FileDownloadInfo fileDownloadInfoInfo);
}

You can check the actual implementation looking at the source code.

WP7BackupServicePhone

This is a bit more interesting: the main purpose of the classes inside this project is to build a wrapper around the WCF service and expose a series of functions you can call to use the backup and restore features in a very simple way, in-fact all you’ll have to do is actually create an instance of the BackupService class, subscribe to the BackupCompleted and RestoreCompleted events and call the PerformBackup() or PerformRestore() functions.

The main problem here is how to make synchronous what synchronous is not (the calls to WCF proxy functions), without blocking the responsiveness of the UI (yes, we’ll use a background thread) and preserving the order of operations; also we’ll need a way to execute event handling code inside the UI thread to be able to update the controls status (without incurring in cross thread operation exceptions).

Let’s consider the backup operation (the restore stage works more or less the same): we need to simulate synchronous calls because while we are uploading a list of files we do not want to open too many connections to the server, so we’ll move just one file at a time in this first release; here’s the code that recursively explore the IsolatedStorage starting from a known location (pass in string.empty as second argument to explore the whole IsolatedStorage of the application):

private void UploadFiles(IsolatedStorageFile store, string rootPath, bool resursive)
{
	var files = store.GetFileNames(rootPath + "\\*");
	foreach (var file in files)
		UploadBytes(Path.Combine(rootPath, file));
	if (!resursive)
		return;
	// get directoryies
	var dirs = store.GetDirectoryNames(rootPath + "\\*");
	foreach (var dir in dirs)
	{
		string path = Path.Combine(rootPath, dir);
		UploadFiles(store, path, true);
	}
}

The UploadBytes() function internally makes asynchronous calls to the WCF proxy service to upload the file information and bytes to the server, to make this function appear as synchronous we can use an AutoResetEvent: we will wait on this variable just after making the asynchronous call and we will signal it when the asynchronous operation completes, here’s some code:

...
private readonly AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
...
private void UploadBytes(string filename)
{
	_isoStorTemp = new IsolatedStorageFileStream(filename, FileMode.Open, IsolatedStorageFile.GetUserStoreForApplication());
	FileUploadInfo fileUploadInfo = new FileUploadInfo();
	fileUploadInfo.Application = _configuration.Application;
	fileUploadInfo.Set = _configuration.BackupSet;
	fileUploadInfo.Name = filename;
	fileUploadInfo.Bytes = ReadAll(_isoStorTemp);
	_wcfClient.UploadBytesAsync(fileUploadInfo, filename);
	// stop our thread until we get notified by the callback 
	_autoResetEvent.WaitOne();
}

void UploadBytesCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
	_isoStorTemp.Close();
	_autoResetEvent.Set();
}
...

Wait...I talked about executing this code in another thread..why?!? The problem here is in the ‘_autoResetEvent.WaitOne()’ statement: if executed in the same thread of the UI it will end up hanging everything! So we can take advantage of the ThreadPool and some lambda expression to invoke our backup functions in another thread:

/// <summary>
/// this function backs up your entire iso storage (only the sections the application can have safe access to)
/// </summary>
public void PerformBackup()
{
	ThreadPool.QueueUserWorkItem(o =>
		{
			// first-off prepare the backup set and wait for the server to say ok
			_wcfClient.InitBackupSetCompleted += InitBackupSetCompleted;
			_wcfClient.InitBackupSetAsync(_configuration.Application, _configuration.BackupSet);
			_autoResetEvent.WaitOne();

			// start exploring the IsoStorage for file structures to replicate
			using (var store = IsolatedStorageFile.GetUserStoreForApplication())
			{
				string path = string.Empty; // takes the root
				// process the files
				UploadFiles(store, path, true);
			}
			OnBackupCompleted(EventArgs.Empty);
		});
}

The last problem to solve is how to notify the UI from a background thread without incurring in exceptions; this is pretty easy to do in Silverlight/WP7 using the default Dispatcher you can access through Deplayment.Current.Dispatcher:

public event EventHandler BackupCompleted;

private void OnBackupCompleted(EventArgs e)
{
	Deployment.Current.Dispatcher.BeginInvoke(() =>
       	{
       		EventHandler handler = BackupCompleted;
       		if (handler != null) handler(this, e);
       	});
}

There we go! Our backup solution is complete now.

The last thing to see is how to use this piece of software:

  • Add a reference to WP7BackupServicePhone to your project.
  • Add the following information to your ServiceReference.ClientConfig file (or create one if you don’t have it)
    <configuration>
        <system.serviceModel>
            <bindings>
                <basicHttpBinding>
                    <binding name="BasicHttpBinding_IBackupService" maxBufferSize="2147483647"
                        maxReceivedMessageSize="2147483647">
                        <security mode="None" />
                    </binding>
                </basicHttpBinding>
            </bindings>
            <client>
                <endpoint address="http://192.168.1.10:1024/WP7BackupService"
                    binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IBackupService"
                    contract="Service.IBackupService" name="BasicHttpBinding_IBackupService" />
            </client>
        </system.serviceModel>
    </configuration>

    don’t worry about the address it will be overridden at runtime.
  • You can now create instances of the BackupService class, wire up to the events and call the PerformBackup() and PerformRestore() functions, like this
    private void BackupClick(object sender, EventArgs e)
    {
    	BackupService srvc = new BackupService(
    		new BackupServiceConfiguration
    			{
    				Application="bcc", BackupSet = "Test", WcfService = "http://192.168.1.10:1024/WP7BackupService"
    			});
    	srvc.BackupCompleted += SrvcBackupCompleted;
    	srvc.PerformBackup();
    }
    
    static void SrvcBackupCompleted(object sender, EventArgs e)
    {
    	MessageBox.Show("Backup Completed");
    }
    
    private void RestoreClick(object sender, EventArgs e)
    {
    	BackupService srvc = new BackupService(
    		new BackupServiceConfiguration
    			{
    				Application = "bcc", BackupSet = "Test", WcfService = "http://192.168.1.10:1024/WP7BackupService"
    			});
    	srvc.RestoreCompleted += SrvcRestoreCompleted;
    	srvc.PerformRestore();
    }
    
    static void SrvcRestoreCompleted(object sender, EventArgs e)
    {
    	MessageBox.Show("Restore Completed, Restart the application please.");
    }

The full source code for these two project is available on CodePlex at this url:

http://wp7backupservice.codeplex.com/

The project still miss some things, like better notifications and a sample for the usage, but in the spirit of ‘deliver it early’ I decided to post it as is right now.



WP7, WCF, IsolatedStorage, Backup

4 comments

  1. #1 da Alberto - Friday July 2011 alle 05:21

    Nice, but does this means you have a webservice deployed in a home server or is it a cloud server say like azure??

  2. #2 da alessandro giorgetti - Saturday July 2011 alle 12:11

    That project creates an .exe that self hosts the webservice in your own computer, just download the source, compile it an run.
    You will have the web service available in your local network.
    You can have some informations on how to use it at this link: http://www.primordialcode.com/wp7-business-cards which is related to one of my WP7 apps.

  3. #3 da mdrapps - Wednesday September 2011 alle 04:42

    The WP7 Backup Service is very well done. I will be using it for the backend of my WP7 apps to backup data. Would be nice if the service could list the files in the backup folder on the pc. Better error handling with raised event messages in the backup and restore methods would be good too. Thank you!

  4. #4 da alessandro giorgetti - Thursday September 2011 alle 09:44

    Thank you for your suggestions, I hope to find some time soon to add these (and other) things to this project and to end the conversion of my apps to Mango.

All fields are required and you must provide valid data in order to be able to comment on this post.


(will not be published)
(es: http://www.mysite.com)


  1. #1 da http://www.alvinashcraft.com/2011/02/15/dew-drop-february-15-2011/

    Dew Drop &ndash; February 15, 2011 | Alvin Ashcraft&#039;s Morning Dew