(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Caching of, in, and around your Silverlight application (part 2)

(3 votes)
Kevin Dockx
>
Kevin Dockx
Joined Nov 15, 2010
Articles:   19
Comments:   8
More Articles
2 comments   /   posted on Nov 25, 2010

This article is compatible with the latest version of Silverlight.

This is the second in a 3-part series of articles about caching & Silverlight.

The first part was about XAP & Assembly caching, which you can find here. In this part, we’ll see how we can leverage the Isolated Storage for caching.

You can download the source code here. 

Reusing data throughout your Silverlight application

But first, I’d like to tackle another issue I tend to get a lot of questions about: sharing data across different parts of your code, e.g.: across different ViewModels. Most of the people who ask me this type of question come from an ASP .NET / Web Application background, where you’ve got different pages in the same application, and you’d typically use something like Application State, Session State or even query strings to share data across different pages, and to avoid refetching it on each page (e.g.: you’re caching the data). If that’s what you’re used to working with, it’s very understandable you’re wondering what the Silverlight equivalent is.

The keyword here is: state. As you know, (X)HTML is stateless: once you post your page to the server to request a new/changed version, the state of your page (actually: the state of your complete application) is lost. ASP .NET works around this by providing you with various means to get some kind of state: ViewState (page level), Session state (user-specific) or Application state (app-wide, across different sessions). In essence, these techniques try to solve the fact that HTML is stateless by providing you with means to achieve some kind of state in your web application. Data you need across different sessions, for example: a list of countries for a combobox, would typically be saved in the Application State. Data that’s user-specific could be saved in the Session state, et cetera.

Fast forward to Silverlight. One of the big transitions one has to make when coming from a web background to Silverlight programming is getting used to the fact that Silverlight is stateful, not stateless: there’s no need for ViewState, Session state or Application state. If you look at it this way, programming a Silverlight application has much more in common with programming a desktop application than a web application, odd as it may seem. I’ve actually noticed that people coming from programming Windows Forms can often transition faster to correctly programming Silverlight applications, exactly because of this.

So: to share data across different ViewModels, what you need is a part of your application which is accessible by all your ViewModels. A typical way to solve this is to provide your application with a LocalStateContainer class. This is a static class which contains static properties. These properties hold your application-wide data – once you need them in a ViewModel, add a property to your ViewModel which gets its data from the LocalStateContainer.

Let’s assume our application needs to get a list of cities from a service. This list of cities should be available to all ViewModels in your application. We’ll start out by creating a WCF Service to get the list of cities. Add a City class to your Web application, and add a new Silverlight-enabled WCF Service which implements one operation: GetCities:

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = 
    AspNetCompatibilityRequirementsMode.Allowed)] public class CityService { [OperationContract] public List<City> GetCities() { // create a new generic list to hold the cities. List<City> cityList = new List<City>(); cityList.Add(new City() { ID = "ANT", Description = "Antwerp" }); cityList.Add(new City() { ID = "BRU", Description = "Brussels" }); cityList.Add(new City() { ID = "GHE", Description = "Ghent" }); cityList.Add(new City() { ID = "AMS", Description = "Amsterdam" }); cityList.Add(new City() { ID = "PAR", Description = "Paris" }); cityList.Add(new City() { ID = "LON", Description = "London" }); cityList.Add(new City() { ID = "BAR", Description = "Barcelona" }); return cityList; } }

Next, add a Service Reference to this service from your Silverlight application.

The LocalStateContainer class should now contain a static Observable Collection of cities. In the MainViewModel, we’ll fetch the data from the service, but we’ll only fetch the data when the city list is still empty. Once the cities have been fetched, we’ll put them in the LocalStateContainers’ City property.

internal void GetCitiesFromServiceOrIsolatedStorage()
{
    // is there any country data in the Isolated Storage?
    if (userIsolatedStorageSettings.Contains("CachingCities"))
    {
        Cities = (ObservableCollection<City>)userIsolatedStorageSettings["CachingCities"];
    }
    else
    {
        // call WCF Service
        CityServiceReference.CityServiceClient client = new CityServiceClient();
        client.GetCitiesCompleted += (send, args) =>
        {
            if (args.Error == null)
            {
                Cities = args.Result;
                userIsolatedStorageSettings["CachingCities"] = 
                    new ObservableCollection<City>(args.Result); userIsolatedStorageSettings.Save(); } }; client.GetCitiesAsync(); } }

From now on, your city list is available application-wide. If you need access to this list, just add a property to a ViewModel which gets the cities from that LocalStateContainers’ Cities property.

For demo purposes, I’m fetching the list of cities in my MainViewModel. Depending on the type of application you’re designing, you could change this. One way would be to fetch the application-wide data right after your application has been started. Another way would be to fetch each list the first time it’s needed through a method in your LocalStateContainer class. There is no “right way” which works for each and every application: the “right way” always depends on the type of application you’re designing.

Reusing data across different Silverlight sessions.

In the example above, we’ve made sure we only need to fetch the application-wide data once. However: your data isn’t persisted across different Silverlight sessions, meaning that every time a client restarts his application, the data will have to be fetched again. It would be nice to able to save data across different sessions – and this is where the Isolated Storage comes into play.

From a Silverlight application, you can access the Isolated Storage on the client machine. The best way to look at this is as a virtual file system, which stores data in a hidden folder on the client machine. Each Silverlight application is allocated a part of Isolated Storage (by default: 1MB per application).

Through System.IO.IsolatedStorage.IsolatedStorageSettings, you can access the ApplicationSettings. Now you’ve got a Dictionary<TKey, TValue> in which you can store data and/or from which you can fetch data. First, we’ll check if the dictionary already contains an item with key “CachingCities” (a unique key). If it does, we’ll fetch the data from Isolated Storage by filling the Cities property on our MainViewModel with the value from the CachingCities dictionary entry. If there’s no item with that key present, we fetch the data from our service, and save the data in Isolated Storage.

internal void GetCitiesFromServiceOrIsolatedStorage()
{
    // is there any country data in the Isolated Storage?
    if (userIsolatedStorageSettings.Contains("CachingCities"))
    {
        Cities = (ObservableCollection<City>)userIsolatedStorageSettings["CachingCities"];
    }
    else
    {
        // call WCF Service
        CityServiceReference.CityServiceClient client = new CityServiceClient();
        client.GetCitiesCompleted += (send, args) =>
        {
            if (args.Error == null)
            {
                Cities = args.Result;
                userIsolatedStorageSettings["CachingCities"] = 
                    new ObservableCollection<City>(args.Result); userIsolatedStorageSettings.Save(); } }; client.GetCitiesAsync(); } }

Once the list is in Isolated Storage, the service call won’t have to be executed, even between different Silverlight sessions.  Our application will display the list of cities:

image

One note of caution: although the Isolated Storage is hidden on the client machine, a user can still access the folder, so data stored isn’t completely secure. You can however use the Cryptography classes to encrypt the data you’re storing.

 

Clearing the Isolated Storage and asking for higher quota.

The Isolated Storage can be cleared as well, with the following statement:

internal void ClearIsolatedStorage()
{
    userIsolatedStorageSettings.Clear();
}

If you’re only saving small amounts of data, the default 1MB storage space will probably be sufficient. However, if you’re saving large sets of data, you might require more. This requires user interaction: you need to ask the client if he wants to increase the amount of Isolated Storage space available to your application by executing the IncreaseQuotaTo command on the current User Store.

internal void IncreaseQuota()
{
    using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
    {
        // ask for 5MB 
        isf.IncreaseQuotaTo(5242880);
    }
}

This will result in the user getting a popup like this:

image

To check the amount of space available to your application, right-click your Silverlight application, select “Silverlight” and navigate to the Application Storage tab. You’ll get an overview of the current usage & quote for all Silverlight applications using Isolated Storage on the current machine.

 

What about sharing the Isolated Storage between different applications?

Image the following scenario: you’re working for a company which uses a number of different Silverlight applications. All these applications use some shared data, for example: a list of employees, a list of company locations, … This is data which is pretty static, so you’d like to cache this data. However, if you’re using the Isolated Storage for this as we did in the previous paragraphs, each Silverlight application will have to fetch the list of employees, locations, … even though they are the same for each application.

There’s a solution to this problem: sharing data from your Isolated Storage across applications. In the previous paragraphs, we’ve used IsolatedStorageSettings.ApplicationsSettings. There’s another user store available: the user store for the current site. This store is shared across all Silverlight applications hosted on the same site.

You can access it through IsolatedStorageSettings.SiteSettings. However, in this case I prefer using the storage file directly (as you might know, IsolatedStorageSettings.ApplicationSettings and SiteSettings are just easy ways to access the underlying file, doing the serialization from/to objects in Isolated Storage for you, instead of having you write them yourself). I do this because IsolatedStorageSettings will save the data in its finalizer, which can actually result in different applications overwriting each others’ site settings with their own, resulting in empty data. By using the lower level API’s, you can make sure you won’t have this problem.

Let’s change the code from the previous example a bit, so we’re storing the data in the Site store for the current user:

internal void SaveCitiesToSiteStorage()
{
    // get the city list and save it to the site store, so other applications on
    // the same site can access it.
    // call WCF Service
    CityServiceReference.CityServiceClient client = new CityServiceClient();
    client.GetCitiesCompleted += (send, args) =>
    {
        if (args.Error == null)
        {
            using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForSite())
            {
                IsolatedStorageFileStream fileStream = null;
                fileStream = isf.OpenFile(
                    "mySiteStore",
                    System.IO.FileMode.Create,
                    System.IO.FileAccess.ReadWrite); XmlSerializer xmlSerializer = new XmlSerializer(
                    typeof(ObservableCollection<City>),
                    "Cities"); xmlSerializer.Serialize(fileStream, args.Result); fileStream.Flush(); fileStream.Close(); fileStream.Dispose(); } } }; client.GetCitiesAsync(); }

If we now add another Silverlight application to our solution, hosted on the same site, we can access the data saved to Isolated Storage from the first application:

public MainViewModel()
{
    // get city list from site settings
    using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForSite())
    {
        if (isf.FileExists("mySiteStore"))
        {
            // fetch the data 
            IsolatedStorageFileStream fileStream = isf.OpenFile(
                "mySiteStore",
                System.IO.FileMode.OpenOrCreate,
                System.IO.FileAccess.ReadWrite); XmlSerializer xmlSerializer = new XmlSerializer(
                typeof(ObservableCollection<City>),
                "Cities"); Cities = (ObservableCollection<City>)xmlSerializer.Deserialize(fileStream); fileStream.Close(); fileStream.Dispose(); } } }

So it’s pretty easy to share data across different Silverlight applications, but: they must be hosted on the same site.

 

Conclusion

This concludes the second part of this article, in which we’ve looked into various ways of leveraging the Isolated Storage to cache your data between sessions and/or between different applications.  Stay tuned for the next (and last) part, which will tackle server-side caching.

 

About the author

Kevin Dockx lives in Belgium and works at RealDolmen, one of Belgium's biggest ICT companies, where he is a technical specialist/project leader on .NET web applications, mainly Silverlight, and a solution manager for Rich Applications (Silverlight, Windows Phone 7 Series, WPF, Surface). His main focus lies on all things Silverlight, but he still keeps an eye on the new developments concerning other products from the Microsoft .NET (Web) Stack. As a Silverlight enthusiast, he's a regular speaker on various national and international events, like Microsoft DevDays in The Netherlands, Microsoft Techdays in Portugal or on BESUG events (the Belgian Silverlight User Group). Next to that, he also authored a best-selling Silverlight book, Packt Publishing's Silverlight 4 Data and Services Cookbook, together with Gill Cleeren. His blog, which contains various tidbits on Silverlight, .NET, and the occasional rambling, can be found at http://blog.kevindockx.com/.


Subscribe

Comments

  • -_-

    RE: Caching of, in, and around your Silverlight application (part 2)


    posted by xiaoqishao on Nov 29, 2010 10:54
    It looks nice.
  • vimal_amaldas

    Re: Caching of, in, and around your Silverlight application (part 2)


    posted by vimal_amaldas on Aug 08, 2012 14:09
    What if I want to cache files - like images,xml docs ?

Add Comment

Login to comment:
  *      *       

From this series