Applying server-side caching via a Ria Service

Introduction

Caching data is a quite popular in regular web technology (e.g. asp.net).  Although LightSwitch has no native support for caching, it can be easily accomplished via a dedicated Ria Service.

When to use caching

There is no clear rule of thump for using caching and for when using no caching. Personally, I would only use it,

  • for data which is updated infrequently and
  • for read-only data.

If you apply these simple rules, you will love caching. Otherwise.. you’ll hate it :)

How?

I presume here that you are familiar with setting up a RIA service in a LightSwitch project.

I have in LightSwitch a Customer entity with a firstname and lastname field. I create in the RiaService a DTO (data transfer object) called “MyCacheDTO” which is doing nothing more than projecting the two customer fields to this new DTO.

This is the code for the Ria Service:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;
using System.Text;
using System.Web;
using System.Web.Caching;

namespace Ria
{
    public class MyCacheDTO
    {
        [Key]
        public int ID { get; set; }
        public string MyName { get; set; }
    }

    [EnableClientAccess]
    public class CachService : LightSwitchDomainServiceBase
    {
        [Query(IsDefault = true)]
        public List<MyCacheDTO> GetCustomers()
        {
            return GetData();
        }

        private List<MyCacheDTO> GetData()
        {
            List<MyCacheDTO> result;
            string cacheKey = "Customers";
            if (HttpContext.Current.Cache.Get(cacheKey) == null)
            {
                string timeStamp = DateTime.Now.ToString();

                result = new List<MyCacheDTO>();
                foreach (var item in this.Context.Customers.ToList<ApplicationData.Implementation.Customer>())
                {
                    MyCacheDTO newDto = new MyCacheDTO { ID = item.Id, MyName = item.LastName + " " + timeStamp };
                    result.Add(newDto);
                }

                HttpContext.Current.Cache.Add(cacheKey, result, null, DateTime.Now.AddMinutes(1),
                    Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
            }
            else
            {
                result = (List<MyCacheDTO>)HttpContext.Current.Cache[cacheKey];
            }
            return result;
        }
    }
}

I apply a little trick so that you can easily verify that the caching is actually working correctly. When I project the data to the DTO I add a time stamp to the lastName field. This allows you to verify if the cache was “hit”‘.

Update November 1, 2012: Important here to NOT use an IQueryable, but make use of a List. When you use an IQueryable, you will simply cache the Query and not the actual data !

Also for testing purposes, I have set the cache expiration to one minute, but this is probably you want to change when using it in production.

I’m in the habit of letting my RIA services derive from a base class “LightSwitchDomainServiceBase”, which I add over here for completeness:

using ApplicationData.Implementation;
using System;
using System.Collections.Generic;
using System.Data.EntityClient;
using System.Linq;
using System.ServiceModel.DomainServices.Server;
using System.Text;

namespace Ria
{
    public class LightSwitchDomainServiceBase : DomainService
    {
        #region Database connection
        private ApplicationDataObjectContext m_context;
        public ApplicationDataObjectContext Context
        {
            get
            {
                if (this.m_context == null)
                {
                    string connString =
                        System.Web.Configuration.WebConfigurationManager
                        .ConnectionStrings["_IntrinsicData"].ConnectionString;
                    EntityConnectionStringBuilder builder = new EntityConnectionStringBuilder();
                    builder.Metadata =
                        "res://*/ApplicationData.csdl|res://*/ApplicationData.ssdl|res://*/ApplicationData.msl";
                    builder.Provider =
                        "System.Data.SqlClient";
                    builder.ProviderConnectionString = connString;
                    this.m_context = new ApplicationDataObjectContext(builder.ConnectionString);
                }
                return this.m_context;
            }
        }
        #endregion
        // Override the Count method in order for paging to work correctly
        protected override int Count<T>(IQueryable<T> query)
        {
            return query.Count();
        }
    }
}

 

Create now a new screen for the cacheDTO and enjoy the server cache !