Introduction
There is a very basic need to cache some data at clientside in Silverlight, when we get data using WCF service and want that data to reside at the client side so that the system will not fetch the data every time from the server. This will make sense when we know that the data is kind of static and will not change frequently.
To make use of IsolatedStorage
, a developer has to create a file stream and save the data. Ideally in the real world, all data will be kept in memory as an object of some type. Now to store this object in IsolatedStorage
, one has to first deserialize it and store it in files and define file names for them. Later this data will be read from the files and will be kept in memory again in the form of an object.
To simplify the above task, I have combined the functionality of IsolatedStorage
and DataContractSerializer
and created the ClientStorage
class which helps the user to store any object directly in the IsolatedStorage
, i.e. making object persistent, an interesting feature of OOPs. To achieve a feature of OOPs, one must follow the OOPs approach rather than writing the same code again and again for different objects. This piece of code can be clubbed together and I have named it as ClientStorage
.
Background
Silverlight provides two classes, one is IsolatedStorage
and another is DataContractSerializer
.
Silverlight provides IsolatedStorage
class to store data at the client machine. With isolated storage, data is isolated by user and by assembly. Credentials such as the origin or the strong name of the assembly determine assembly identity. Data can also be isolated by application domain, using similar credentials.
DataContractSerializer
class is used to serialize and deserialize instances of a type into an XML stream
or document. For example, you can create a type named Person
with properties that contain essential data, such as a name and address. You can then create and manipulate an instance of the Person
class and write all of its property values in an XML document for later retrieval, or in an XML stream for immediate transport. Most important, the DataContractSerializer
is used to serialize and deserialize data sent in Silverlight version 2 messages. Apply the DataContractAttribute
attribute to classes, and the DataMemberAttribute
attribute to class members to specify properties and fields that are serialized.
Inside ClientStorage
ClientStorage
is a singleton class that is basically a indexed collection which helps in fetching the objects on the basis of key. It automatically serializes the object and also stores the type of the object, so that it can be de-serialized later. It gives Index based object fetching mechanism which makes life easier.
It contains a Dictionary
of key and object. It writes all the keys to keyNames.txt and each object in a separate file. Key names are unique so all the objects will be stored in the file name as <key_name>object.xml.
An indexer is written to fetch the value via providing the key in the index. As soon as any new key is added to the ClientStorage
, it adds it to the Dictionary
and serializes the Dictionary
and stores it in keynames.txt file. Then the newly added object is serialized and stored in a separate file. When object is retrieved from ClientStorage
, it first looks for the key existence and then if the object is null
, reads the object's XML file, deserializes the object, updates in Dictionary
and then returns the object.
private object Retreive(string key)
{
object value=null;
if (CheckForFileExistence(key + KEY_OBJECT_FILE) &&
keysNTypes.ContainsKey(key))
{
if (keysNTypes[key].StoredObject == null)
{
try
{
using (IsolatedStorageFileStream iStream =
new IsolatedStorageFileStream(key + KEY_OBJECT_FILE,
FileMode.OpenOrCreate, isoStore))
{
if (iStream != null)
{
try
{
DataContractSerializer serializer =
new DataContractSerializer
(keysNTypes[key].TypeofObject);
value = serializer.ReadObject(iStream);
}
catch (Exception)
{
}
keysNTypes[key].StoredObject = value;
iStream.Close();
}
}
}
catch (FileNotFoundException)
{
throw new KeyNotFoundException();
}
}
else
{
value = keysNTypes[key].StoredObject;
}
}
return value;
}
While instantiating the ClientStorage
object, it reads the keys file and creates the Dictionary
without their objects. When retrieval request comes for any object, then only it fetches the object from the XML file. This way I am minimizing the effort of loading/instantiating the ClientStorage
object.
private void ReadKeys(IsolatedStorageFile isoStore)
{
IsolatedStorageFileStream iStream =
new IsolatedStorageFileStream(ISOLATED_KEY_FILE_NAME,
FileMode.Open, isoStore);
DataContractSerializer serializer =
new DataContractSerializer(keysNTypes.GetType());
keysNTypes = serializer.ReadObject(iStream) as Dictionary<string>;
}
Using the Code
The approach is simple and following the habit of using Session
and ViewState
. When we use Session
to store any object in ASP.NET, the developer does not bother where this information will be kept. She/he keeps her/his objects in session
when she wanted them to be available throughout the life time of user's session. The same applies to IsolatedStorage
, when developer wants to store something per user/application wide data on client machine that would be available cross browser to the application independent of session
like persistent for always until user deletes them manually. Then why to complicate things, why not something like ClientStorage[keyname] = object;
and it's all done.
ClientStorage[keyname] = object;
object = ClientStorage[keyname];
TODO
Next, it can be improved/enhanced further to provide versioning support for the objects stored in ClientStorage
so that it helps in identifying the stale objects in the ClientStorage
and allows to re-fetch/re-creation of that object.
I know this can be improved further, any suggestions or feedback will be highly appreciated.
History
- 28th June, 2009: Initial post