Caching data and improve performance

One of the most emphasized issues on web-development lately is caching data

There are a couple of options to think about if you want to improve your app's performance and going to cache some data. One of them is the ObjectCache class from the framework 4 and later. A litle example how to use the ObjectCache class: create a ObjectCache class in your business layer

    /// 
    /// Cache objects wich caches
    /// the different lists 
    /// 
    public class Cache
    {
        public static readonly ObjectCache cache = MemoryCache.Default;
    }

Now you can use this class to cache database results in you business methods like this:

public class Salutations
    {
        // consts
        public const string key = "salutations";
        
        // methods
        public List<Salutation> Getall()
        {
            var cachedSalutations = (List<Salutation>)Cache.cache.Get(key);
            if (cachedSalutations == null)
            {
                using (var database = new DataBase())
                {
                    cachedSalutations = database.Salutations.ToList();
                    Cache.cache.Add(key, cachedSalutations, DateTime.Now.AddDays(1));
                }
            }
            return cachedSalutations;
        }

        public void Add(string salutation)
        {
            if (string.IsNullOrEmpty(salutation)) throw new ArgumentNullException(salutation);
            using (var database = new DataBase())
            {
                database.Salutations.Add(new Salutation() { Salutation = salutation, CreateDatum = DateTime.Now, UpdateDatum = DateTime.Now });
                database.SaveChanges();
                Cache.cache.Remove(key);
                Cache.cache.Add(key, database.Aanheffen.ToList(), DateTime.Now.AddDays(1));
            }
        }

If your database isn't just updated by your business object but maybe allso direct or you dont want to have all the refresh cache code in the Crud methods in the businesslayer you can use SqlDependency.

Using SqlDependency to refresh your cache!

SqlDependency allows you to receive notifications when the original data in the database changes so that the cache can be refreshed. Before you can use SqlDependency you need to enable the Sql Broker and set the needed security. Execute the following queries:

ALTER DATABASE database_name SET TRUSTWORTHY ON WITH ROLLBACK IMMEDIATE
ALTER DATABASE database_name SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE
ALTER AUTHORIZATION ON DATABASE::database_name TO sa

These queries just enables the Sql Broker service and sets the right security on your database. Now you have to expand the cache class from the example before and enable the SqlDependency:

public class Cache
    { 

        // privates
        private Dictionary<string, IBusinessObject> businessObjectList = new Dictionary<string, IBusinessObject>();
        private string connectionString =@"Data Source=MyPC\SQLEXPRESS;user id=sa;password=password;Initial Catalog=Database;Integrated Security=false;";
        private static Cache cache = null;
        public static readonly ObjectCache objectCache = MemoryCache.Default;

        // Constructor
        public static Cache Instance()
        {
            if(cache == null)
            {
                cache = new Cache();
            }
            return cache;
        }

        // methods
        public void StartSqlDependency(IBusinessObject businessObject, string sql)
        {
            SqlDependency.Stop(connectionString);
            SqlDependency.Start(connectionString);

            SqlConnection sqlConn = new SqlConnection(connectionString);
                using (SqlCommand sqlCmd = new SqlCommand(@sql, sqlConn))
                {
                    sqlCmd.Notification = null;
                    // start the dependency
                    SqlDependency dependency = new SqlDependency(sqlCmd);
                    // register the dependency
                    businessObjectList.Add(dependency.Id, businessObject);
                    // register event
                    dependency.OnChange += dependency_OnDataChangedDelegate;

                    // open connection and execute command
                    // necessary for registering the dependency
                    sqlConn.Open();
                    sqlCmd.ExecuteReader();
                }
        }

        // catch the dependency data changed
        // event
        private void dependency_OnDataChangedDelegate(object sender, SqlNotificationEventArgs e)
        {
            // get the id from the send ojbect
            string id = ((SqlDependency)sender).Id;
            // query the business object list for the stored business object
            var foundBusinessObjectEntry = businessObjectList.Where( a=> a.Key ==  id).FirstOrDefault();
            if(foundBusinessObjectEntry.Value != null)
            {
                var foundBusinessObject = (IBusinessObject)foundBusinessObjectEntry.Value;
                if(foundBusinessObject != null)
                {
                    // let the business object
                    // refresh the cache
                    foundBusinessObject.Refresh();
                }
            }
        }
    }

The cache class is now changed to a Singleton so the same instance can be used from multiple business objects. The StartSqlDependency method starts the dependency and takes the businessobject as parameter. In this method the business object itself is registered together with the SqlDependency in a dictionairy with all registered dependency's. An event is registered for changes in databases. In the event onDataChangedDelegate the sender object is of type SqlDependency. If we cast it back to a SqlDependency we will find the Id of the current dependency. With that Id we are going to filter the dictionairy to find the businness object and execute the refresh method:

public interface IBusinessObject
    {
        void Refresh();
    }

    public class Salutations:IBusinessObject
    {

        // privates
        Cache cache = Cache.Instance();

        // consts
        public const string key = "salutations";
        public const string query = @"SELECT [Extent1].[Id] AS [Id], [Extent1].[salutation] AS [salutation], [Extent1].[AanmaakDatum] AS [AanmaakDatum], [Extent1].[BewerkDatum] AS [BewerkDatum], [Extent1].[Gebruiker] AS [Gebruiker], [Extent1].[Omschrijving] AS [Omschrijving] FROM [dbo].[solutations] AS [Extent1]";

        // constructor
        public Salutations()
        {
                // Start the sqlDependency 
                cache.StartSqlDependency(this, query);
        }
   
        // methods
        public List GetAll()
        {
            List cachedAaheffen = (List)Cache.objectCache.Get(key);
            if(cachedAaheffen == null)
            {
                using (var database = new DataBase())
                {
                    cachedAaheffen = database.Aanheffen.ToList();
                    Cache.objectCache.Add(key, cachedAaheffen, DateTime.Now.AddDays(1));
                }
            }
            return cachedAaheffen;
        }

        public void Add(string salutation)
        {
            if(string.IsNullOrEmpty(salutation)) throw new ArgumentNullException(salutation);

            using (var database = new DataBase())
            {
                database.Salutations.Add(new Salutation() { Salutation = salutation, AanmaakDatum = DateTime.Now, BewerkDatum= DateTime.Now });
                database.SaveChanges();
            }
        }

        public void Refresh()
        {
            using (var database = new DataBase())
            {
                Cache.objectCache.Remove(key);
                Cache.objectCache.Add(key, database.Salutations.ToList(), DateTime.Now.AddDays(1));
            }
        }

    }

With these classes you build a caching object used by your business layer before it access the datalayer. This can be used with both asp.net webforms and asp.net mvc. With Asp.net mvc there is a easier way to use caching: Http.Context.Current.Cache

Using Http.Contect.Current.Cache

{{opmerking.Naam}}:

{{opmerking.OpmerkingText}}

            

Saving your comment....

Naam is verplicht!
Email is verplicht!
Opmerking is verplicht!