Generic implementation of System.Web.Cache to get or read and add or store items in the cache

by Heathesh 29. November 2010 21:48

I have a bunch of settings stored in the database that I know may never change. In order to read these settings I would prefer not to do a database call everytime I want to get a setting value. So in order to minimize the database calls, I decided to implement a simple manager class to get and add items to the cache.

To start off with, I created my manager class (as static) and called it CacheManager:

    /// <summary>
    /// Cache manager to store and read items in the cache
    /// </summary>
    public static class CacheManager
    {
    }


Next, I added the usings I knew I would need to the class:

using System.Diagnostics;
using System.Reflection;
using System.Text;
using System.Web.Caching;


I'm adding System.Diagnostics and System.Reflection to dynamically create the "keys" I'll be using to cache my items. I created a method that would dynamically generate this key as follows:

        /// <summary>
        /// Gets the cache key for a particular method
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns></returns>
        private static string getCacheKey(params object[] parameters)
        {
            StackTrace stackTrace = new StackTrace();
            MethodBase methodBase = stackTrace.GetFrame(2).GetMethod();
            StringBuilder cacheKey = new StringBuilder();

            //the start of the key will be the full method namespace underscore the method name
            cacheKey.AppendFormat("{0}_{1}", methodBase.ReflectedType.FullName, methodBase.Name);

            //add any parameters as trailing values in the key
            if (parameters != null && parameters.Length > 0)
                foreach (object parameter in parameters)
                    cacheKey.AppendFormat("_{0}", parameter);

            //return the key we've generated
            return cacheKey.ToString();
        }


The "key" you use must be unique for each unique "object" you store in the cache. So the above method will determine the name of the calling method and allows you to pass in all the parameters of that method to create a unique key for every method you have. This should become clearer later on.

The next thing I wanted to do was create the relevant methods to get and add items to the cache, so I created the following two. The comments should be self explanatory:

        /// <summary>
        /// Gets an item from the cache
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static T Get<T>(params object[] parameters)
        {
            //generate the "key" for the cached item
            string key = getCacheKey(parameters);

            //set the return item to it's default
            T returnValue = default(T);

            //make sure the current http context is not null
            if (HttpContext.Current != null)
                if (HttpContext.Current.Cache.Get(key) != null)
                    returnValue = (T)HttpContext.Current.Cache.Get(key);

            //return the value
            return returnValue;
        }

        /// <summary>
        /// Adds the relevant item to the cache and returns it as well
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="itemToCache"></param>
        /// <param name="hoursToCache"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static T Add<T>(T itemToCache, int hoursToCache, params object[] parameters)
        {
            //generate the "key" for the cached item
            string key = getCacheKey(parameters);

            //make sure the current http context is not null
            if (HttpContext.Current != null && itemToCache != null)
                HttpContext.Current.Cache.Add(key, itemToCache, null, DateTime.Now.AddHours(hoursToCache), TimeSpan.Zero, CacheItemPriority.BelowNormal, null);

            //return the cached item
            return itemToCache;
        }


As you can see each method allows you to pass in an array of parameters and that array is then passed to the getCacheKey method and used to generate the unique key. Now to implement my caching, I have a method called "ReadAll" in my SettingsManager class. And this is how I implemented the caching:

        /// <summary>
        /// Reads all the settings and returns a list of them
        /// </summary>
        /// <returns></returns>
        public static List<Setting> ReadAll()
        {
            //try and get the list of items from the cache
            List<Setting> settings = CacheManager.Get<List<Setting>>();

            //if we found something in the cache, return it
            if (settings != null && settings.Count > 0)
                return settings;

            //if we get here, we haven't found anything so connect to the database and populate the list of settings from there, but first initialize the list
            settings = new List<Setting>();

            //now populate from the database
            using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["EngineDatabase"].ConnectionString))
            {
                connection.Open();

                using (SqlCommand command = new SqlCommand("SettingReadAll", connection))
                {
                    command.CommandType = System.Data.CommandType.StoredProcedure;

                    using (SqlDataReader dataReader = command.ExecuteReader())
                        while (dataReader.Read())
                            settings.Add(populateSettingFromReader(dataReader));
                }

                connection.Close();
            }

            //now add the list to the cache for 24 hours, and return it
            return CacheManager.Add<List<Setting>>(settings, 24);
        }


The "populateSettingFromReader" is a simple method I created to return a "Setting" object from a data reader as follows:

        /// <summary>
        /// Populates a setting object from the data reader
        /// </summary>
        /// <param name="dataReader"></param>
        /// <returns></returns>
        private static Setting populateSettingFromReader(SqlDataReader dataReader)
        {
            return new Setting
            {
                Id = Convert.ToInt32(dataReader["Id"]),
                Name = Convert.ToString(dataReader["Name"]),
                Value = Convert.ToString(dataReader["Value"])
            };
        }


My "Setting" class is a simple entity class that looks like so:

    /// <summary>
    /// Setting entity
    /// </summary>
    public class Setting
    {
        /// <summary>
        /// Gets or sets the Id
        /// </summary>
        public int Id { get; set; }
        /// <summary>
        /// Gets or sets the Name
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Gets or sets the Value
        /// </summary>
        public string Value { get; set; }
    }


As you can see the "ReadAll" method has no parameters so you don't get to see how the getCacheKey method works with parameters. However the cache key generated for this method would be something like: "[Namespace].SettingsManager_ReadAll".

To illustrate what the getCacheKey would return for a method with parameters, I created a method that would only retrieve one setting at a time using the setting's name as a parameter:

        /// <summary>
        /// Reads the setting for a specified setting name
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static Setting ReadByName(string name)
        {
            //get the value from the cache passing in the name as a parameter to generate the unique key
            Setting setting = CacheManager.Get<Setting>(name);

            if (setting != null)
                return setting;

            //if we get here, we haven't found anything so connect to the database and populate the list of settings from there
            using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["EngineDatabase"].ConnectionString))
            {
                connection.Open();

                using (SqlCommand command = new SqlCommand("SettingReadByName", connection))
                {
                    command.CommandType = System.Data.CommandType.StoredProcedure;
                    command.Parameters.Add(new SqlParameter("@Name", name));

                    using (SqlDataReader dataReader = command.ExecuteReader())
                        if (dataReader.Read())
                            setting = populateSettingFromReader(dataReader);
                }

                connection.Close();
            }

            //here I call the cache method passing in the "name" variable passed into this method as a parameter
            //which will be used as part of the cache key
            return CacheManager.Add<Setting>(setting, 24, name);
        }


I called this method like so:

            //get the theme setting value
            Setting setting = SettingsManager.ReadByName("Theme");

Now as you can see the value of the parameter I'm passing in is "Theme". So for this method the cache key would be something like: "[Namespace].SettingsManager_ReadByName_Theme". Note the trailing "_Theme".

Happy caching!

Tags: , , , , , , , ,

Development | .Net | Visual Studio 2010 | VS2010

Simple XML Serialization and Deserialization using generics

by Heathesh 28. October 2010 21:06

I wanted to create a standard manager class I could use to serialize and deserialize my entity classes regardless of the type of class. I wanted the manager class to be able to write the XML out to a file, or read it from a file and return the populated class object. In order to do this, I started off with the methods to Serialize a class. But first, you need to add the following usings:

using System.IO;
using System.Text;
using System.Xml.Serialization;

Next, add the methods:

        /// <summary>
        /// Serialize class into an XML string
        /// </summary>
        /// <typeparam name="T">Type of the class</typeparam>
        /// <param name="classToSerialize">Class to serialize</param>
        /// <returns>The serialised string</returns>
        public static string SerializeToXML<T>(T classToSerialize) where T : class
        {
            XmlSerializer serializer = new XmlSerializer(classToSerialize.GetType());
            StringBuilder stringBuilder = new StringBuilder();

            using (StringWriter writer = new StringWriter(stringBuilder))
            {
                serializer.Serialize(writer, classToSerialize);
                writer.Close();
            }

            return stringBuilder.ToString();
        }

        /// <summary>
        /// Serialize class directly to file
        /// </summary>
        /// <typeparam name="T">Type of the class</typeparam>
        /// <param name="fileName">File name and path</param>
        /// <param name="classToSerialize">Class to serialize</param>
        public static void SerializeXMLToFile<T>(string fileName, T classToSerialize) where T : class
        {
            using (StreamWriter streamWriter = new StreamWriter(fileName, false, Encoding.Unicode))
            {
                streamWriter.Write(SerializeToXML<T>(classToSerialize));
                streamWriter.Flush();
                streamWriter.Close();
            }
        }


The comments should be self explanatory, take special note of the "where T : class" clause, I added that in as a safety clause so this methods could only be run on class objects and not value types. Next I added the deserialize methods:

        /// <summary>
        /// Deserialize class
        /// </summary>
        /// <typeparam name="T">Type of object to return</typeparam>
        /// <param name="serializedObject">Serialized xml string</param>
        /// <returns>Deserialized class</returns>
        public static T DeserializeXML<T>(string serializedObject) where T : class
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            T result = default(T);

            using (MemoryStream input = new MemoryStream(System.Text.Encoding.Unicode.GetBytes(serializedObject)))
            {
                input.Position = 0;
                result = (T)serializer.Deserialize(input);
                input.Close();
            }

            return result;
        }
        /// <summary>
        /// Deserialize class from file
        /// </summary>
        /// <typeparam name="T">Type of object to return</typeparam>
        /// <param name="filePath">File name and path</param>
        /// <returns>Deserialized class</returns>
        public static T DeserializeXMLFile<T>(string filePath) where T : class
        {
            T result = default(T);

            if (File.Exists(filePath))
            {
                string fileContents = System.IO.File.ReadAllText(filePath);

                if (fileContents.Length > 0)
                    result = DeserializeXML<T>(fileContents);
            }

            return result;
        }


That was pretty much it. I put the methods in a static class I named XmlManager and I've been using it to serialize and deserialize my entity classes. Simple example of how to use this:

            //create a sample person object to serialize
            Person person = new Person
            {
                Id = 1,
                FirstName = "Heathesh",
                Surname = "Bhandari",
                DateOfBirth = new DateTime(1980, 1, 1)
            };
           
            //serialize the class to xml
            string serializedXml = XmlManager.SerializeToXML<Person>(person);
            Console.WriteLine(serializedXml);

            //deserialize the xml string to the class object
            Person newPerson = XmlManager.DeserializeXML<Person>(serializedXml);
            Console.WriteLine("{0} {1} {2}", person.Id, person.FirstName, person.Surname);


Happy serializing!

Tags: , , , , ,

Development | .Net | Visual Studio 2010 | VS2010



Powered by BlogEngine.NET 1.5.0.7 (with enhancements by Heathesh)
Theme by Mads Kristensen (with tweeks by Heathesh)

Certifications

Microsoft Certified Professional

Microsoft Certified Technology Specialist

Answer Questions

 

Tag cloud

Calendar

<<  August 2017  >>
MoTuWeThFrSaSu
31123456
78910111213
14151617181920
21222324252627
28293031123
45678910

View posts in large calendar

http://heathesh.com