James Ashley posted a great tip on simplifying tombstoning by abstracting IsolatedStorageSettings.ApplicationSettings and PhoneApplicationService.Current.State to a common interface IDataStorage. This makes storing either temporary state data or more permanent settings data much easier with less code.
This was a very good post but the duplication on the two IDataStorage implementations made me cringe a little. (Remember DRY?) I knew there was a better way. I also didn’t need a unique token in my scenario. I was storing one instance of a plain old settings class. This would require token generation (or a static string) for the backup in the closing event and also in my ViewModelLocator where I would restore the settings class. Again, I didn’t like the duplication of the key. I realize I may have a need for a unique key down the rode but I would prefer not to provide one until then.
So I created a base class which holds the core backup/restore logic and takes in a dictionary<string,object>. My two implementations inherit from the base and pass the appropriate dictionary (IsolatedStorageSettings.ApplicationSettings or PhoneApplicationService.Current.State). I made the backup method generic so I could take the type and use it to generate a token if none was passed. This allows me to persist any one type object in the store without a token at a time. If the need arises I can always add a token for multiple type instances. I’m using an optional token parameter on the methods (a new feature for .NET 4).
Here is the code.
public interface IDataStorage
{
bool Backup<T>(T value, string token);
T Restore<T>(string token);
}
public class DataStorageBase
{
protected bool Backup<T>(IDictionary<string, object> bag, T value, string token)
{
if ((object)value == null)
return false;SetToken<T>(ref token);
if (bag.ContainsKey(token))
bag[token] = value;
else
bag.Add(token, value);return true;
}protected T Restore<T>(IDictionary<string, object> bag, string token)
{
SetToken<T>(ref token);if (!bag.ContainsKey(token))
return default(T);return (T)bag[token];
}protected void SetToken<T>(ref string token)
{
if (string.IsNullOrEmpty(token))
token = typeof(T).FullName;
}
}
public class PersistentDataStorage : DataStorageBase, IDataStorage
{
private IsolatedStorageSettings _store;public PersistentDataStorage()
{
_store = IsolatedStorageSettings.ApplicationSettings;
}public bool Backup<T>(T value, string token = null)
{
var output = base.Backup<T>(_store, value, token);
_store.Save();
return output;
}public T Restore<T>(string token = null)
{
return base.Restore<T>(_store, token);
}
}
public class TransientDataStorage : DataStorageBase, IDataStorage
{
private IDictionary<string, object> _store;public TransientDataStorage()
{
_store = PhoneApplicationService.Current.State;
}public bool Backup<T>(T value, string token = null)
{
return base.Backup(_store, value, token);
}public T Restore<T>(string token = null)
{
return base.Restore<T>(_store, token);
}
}
If you have further improvements or concerns please share them.
Pingback: The best resources for Windows Phone 7 Development
Pingback: Mister Goodcat | Attribute-based transient Tombstoning