Project DescriptionObjectStorageHelper<T> is a Generic class that simplifies storage of data in WinRT applications while still maintaining the async principles of Metro-style apps.
OverviewWhile exploring WinRT I came up with this, ObjectStorageHelper<T>, a Generic class that wraps (and greatly simplifies) use of the
RoamingFolder,
TemporaryFolder &
LocalFolder storage folders within the
ApplicationData class.
With ObjectStorageHelper<T> saving and loading an object to/from disk is now just a simple method call.
I blogged about ObjectStorageHelper<T> at
ObjectStorageHelper<T> – A WinRT utility for Windows 8RestrictionsUnder the covers ObjectStorageHelper<T> uses XML Serialization hence T needs to be an object that can be serialized as XML. There is a useful thread on Stack Overflow
Using .Net what limitations (if any) are there in using the XmlSerializer? that talks about those restrictions better than I ever could.
Saving an objectUsing ObjectStorageHelper<T> saving an object only takes two lines of code
[TestMethod]
public void SaveObject()
{
//Instantiate an object that we want to save
var myPoco = new Poco() { IntProp = 1, StringProp = "one" };
//new up ObjectStorageHelper specifying that we want to interact with the Local storage folder
var objectStorageHelper = new ObjectStorageHelper<Poco>(StorageType.Local);
//Save the object (via XML Serialization) to the specified folder, asynchronously
objectStorageHelper.SaveASync(myPoco);
}
The name of the file that gets stored is determined by the name of the type T specified in ObjectStorageHelper<T> and this in turn means that only one object of type T can be stored in each of the three folders; this is by design.
If you have a need to store multiple objects of T then you could use a List instead like this: ObjectStorageHelper<List<T>> or simply adjust the code herein so that it allows you to specify a filename.
Retrieving an objectRetrieving that object thereafter is equally as easy, just two lines of code
[TestMethod]
public async void LoadObject()
{
//new up ObjectStorageHelper specifying that we want to interact with the Local storage folder
var objectStorageHelper = new ObjectStorageHelper<Poco>(StorageType.Local);
//Get the object from the storage folder
Poco myPoco = await objectStorageHelper.LoadASync();
}
TestsThe supplied source code contains a WinRT test library that tests various scenarios.
FeedbackFeedback is very welcome. You can get hold of me at:
jamie@removethisbit.jamie-thomson.nethttp://sqlblog.com/blogs/jamie_thomson/@jamietThe source code for ObjectStorageHelper<T>If you prefer to copy-and-paste rather than download-and-reference then feel free to do so from below.
using System;
using System.IO;
using System.Threading.Tasks;
using System.Xml.Serialization;
using Windows.Storage;
using Windows.Storage.Streams;
namespace WinRtUtility
{
public enum StorageType
{
Roaming, Local, Temporary
}
public class ObjectStorageHelper<T>
{
private ApplicationData appData = Windows.Storage.ApplicationData.Current;
private XmlSerializer serializer;
private StorageType storageType;
private string FileName(T Obj)
{
return String.Format("{0}.xml", Obj.GetType().FullName);
}
public ObjectStorageHelper(StorageType StorageType)
{
serializer = new XmlSerializer(typeof(T));
storageType = StorageType;
}
public async void DeleteASync()
{
try
{
StorageFolder folder = GetFolder(storageType);
var file = await GetFileIfExistsAsync(folder, FileName(Activator.CreateInstance<T>()));
if (file != null)
{
await file.DeleteAsync(StorageDeleteOption.TryMoveToRecycleBin);
}
}
catch (Exception)
{
throw;
}
}
public async void SaveASync(T Obj)
{
try
{
if (Obj != null)
{
StorageFile file = null;
StorageFolder folder = GetFolder(storageType);
file = await folder.CreateFileAsync(FileName(Obj), CreationCollisionOption.ReplaceExisting);
IRandomAccessStream writeStream = await file.OpenAsync(FileAccessMode.ReadWrite);
Stream outStream = Task.Run(() => writeStream.OpenWrite()).Result;
serializer.Serialize(outStream, Obj);
}
}
catch (Exception)
{
throw;
}
}
public async Task<T> LoadASync()
{
try
{
StorageFile file = null;
StorageFolder folder = GetFolder(storageType);
file = await folder.GetFileAsync(FileName(Activator.CreateInstance<T>()));
IRandomAccessStream readStream = await file.OpenAsync(FileAccessMode.Read);
Stream inStream = Task.Run(() => readStream.OpenRead()).Result;
return (T)serializer.Deserialize(inStream);
}
catch (FileNotFoundException)
{
//file not existing is perfectly valid so simply return the default
return default(T);
//throw;
}
catch (Exception)
{
//Unable to load contents of file
throw;
}
}
private StorageFolder GetFolder(StorageType storageType)
{
StorageFolder folder;
switch (storageType)
{
case StorageType.Roaming:
folder = appData.RoamingFolder;
break;
case StorageType.Local:
folder = appData.LocalFolder;
break;
case StorageType.Temporary:
folder = appData.TemporaryFolder;
break;
default:
throw new Exception(String.Format("Unknown StorageType: {0}", storageType));
}
return folder;
}
private async Task<StorageFile> GetFileIfExistsAsync(StorageFolder folder, string fileName)
{
try
{
return await folder.GetFileAsync(fileName);
}
catch
{
return null;
}
}
}
}