Caching Read Only Collection?

Caching Read Only Collection?

Old forum URL: forums.lhotka.net/forums/t/2688.aspx


sgraham posted on Thursday, April 12, 2007

Has anybody tried caching a read only collection (rather than a NameValueList)?

I've applied the CSLA caching pattern (static list, factory check for null and InvalidateCache), if you will, to one read only collection and it seems to work well.  I'm considering doing the same for more of my read only collections and I'm wondering if there are any issues or concerns to look out for before doing so.  It seems to be that any short list of data that is referential in nature (rarely updated) would perform better with this implementation.  Am I missing something?

Thanks in advance for any responses/help.

RockfordLhotka replied on Thursday, April 12, 2007

It is a perfectly good approach to caching relatively static data.

Remember that the lifetime of a static/Shared field is the lifetime of the AppDomain. In a Windows Forms app, that means the life of the app itself. If the data is likely to change within that timeframe, make sure you have some refresh mechanism.

Also remember that such caching does consume memory. If a read-only list is used rarely, it may not be worth caching because the memory cost may outweight the cost of re-loading the data.

glenntoy replied on Thursday, April 12, 2007

Just a thought, instead of caching it using the static/shared field in Windows Forms app, would it be possible to cache the readonly list into a file so that next time it runs it will just retrieve in file rather than in the DB? This is with assumption that you have the refresh mechanism where in you know when to read the cache data file or from DB.

I'm guessing wild, I think this can be done using binary formatter to serialize the read only list, my question is their a problem in this approach?

RockfordLhotka replied on Thursday, April 12, 2007

You could certainly do this, yes. You’d just need to also catch deserialization exceptions in the case that you’ve updated the business DLL since the cache was last saved to disk, and in that case re-fetch the data from the database.

 

Rocky

 

ajj3085 replied on Friday, April 13, 2007

If you're going to go this route, I suggest looking into the Caching Application Block.  It has built in support for where it stores the cache, and you can have multiple stores per cache (so that memory is prefered, then file, the  you'd read from the db).

HTH
Andy

Moh_Abed replied on Monday, April 16, 2007

Right,

I use Caching Application Block as caching infrastrucure with Isolated Storage / Database persistence, u can make caching transparent by implementing  caching logic in client side DataPortal, (Fetch), i use the idea of marking cacheable read only collections with an attribute to be considered in the cache, also u can configure the expiry policy for each element in the cache, say u need the cache to be invalidated/refreshed every 10 minutes ....

 

sgraham replied on Monday, April 16, 2007

What level are you caching (field, record or collection)?  Rock'y current implementaion caches the entire collection.  The Microsoft Enterprise Library labs show examples of cahcing specific fields.  Are you doing feild level, record level or collection level caching with CSLA?  I'd be curious to see/understand your approach.

Thanks!

chrisc1234 replied on Monday, April 16, 2007

I am currently using the MS App Blocks.  I have created a generic caching class to make it easy to create "strongly-typed" cacching objects (see below):

I then create a Caching class:  MyCacheClass : Caching<MyCslaCollection>

In my collection/list/business class, my fetch factory method looks like this:

        public static MyCslaCollection GetMyCslaCollection ()
        {
            MyCacheClass cache = new MyCacheClass();
            MyCslaCollection  collection = cache.Get();
            if ((collection) == null)
            {
                collection = DataPortal.Fetch<MyCslaCollection>(new Criteria());
                cache.Add(collection);
            }

            // return
            return collection;
        }

 

Also, if an item is removed/added, you can invalidate the cache:

MyCacheClass cache = new MyCacheClass();

cache.Clear();

I like this because it is OOP, strongly-typed and I don't have to worry about mistyping the key name string.

 

using System;
using System.Collections.Generic;
using System.Text;
//
using Microsoft.Practices.EnterpriseLibrary.Caching;
using Microsoft.Practices.EnterpriseLibrary.Caching.Expirations;

namespace Hawkeye.Library.Caching
{
    /// <summary>
    /// Generic class for creating strongly-typed caching objects. 
    /// Uses the MS Enterprise Library Caching Block.
    /// Use the protected Caching() constructor to use the class name T as the key name.
    /// </summary>
    /// <remarks>
    /// <ClassName>Caching : T</ClassName>
    /// <ClassType>Generic Helper Class</ClassType>
    /// <WrittenBy>Chris Clark</WrittenBy>
    /// <DateWritten>01/25/07</DateWritten>
    /// <Description>
    /// Generic class for creating strongly-typed caching objects. 
    /// Uses the MS Enterprise Library Caching Block.
    /// Use the protected Caching() constructor to use the class name T as the key name.
    /// </Description>
    /// <ReviewedBy>Chris Clark 02/14/07</ReviewedBy>
    /// <ApprovedBy></ApprovedBy>
    /// <Modifications>
    ///     <Modification>
    ///         <ModificationDate></ModificationDate>
    ///         <ModificationWorkOrder></ModificationWorkOrder>
    ///         <ModificationDescription></ModificationDescription>
    ///     </Modification>
    /// </Modifications>
    /// </remarks>
    /// <example>
    /// <code>
    ///    // create strongly-typed caching class
    ///    public class TestCaching : Caching&lt;T&gt;
    ///    {
    ///    // ...end class
    ///
    ///    // business list class
    ///    public class TestList
    ///    (
    ///        // encapsulate getting data from
    ///        // cache in a class method
    ///        public static TestList GetTestList()
    ///        {
    ///            TestCaching cache = TestList.Cache;
    ///            TestList list = cache.Get();
    ///            if (list == null)
    ///            {
    ///                list = TestList.Fetch();
    ///                cache.Add(list);
    ///                return list;
    ///            }
    ///            else
    ///            {
    ///                return list;
    ///            }
    ///        }
    /// </code>
    /// </example>

    public class Caching<T>
    {
        #region Declarations

        // const
        /// <summary>
        /// Default timeout in minutes
        /// </summary>
        public const int defaultTimeout = 2;

        // var
        protected string _key;
        protected int _timeOut;

        #endregion

        #region Constructors

        /// <summary>
        /// Constructor.
        /// This uses the name of the class T as the cache key name.
        /// </summary>
        protected Caching() : this((typeof(T)).Name)
        {
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="key">Name of the cache key.</param>
        protected Caching(string key) : this(key, defaultTimeout)
        {
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="key">Name of the cache key</param>
        /// <param name="timeOut">Timeout in minutes</param>
        protected Caching(string key, int timeOut)
        {
            // init
            _key = key;
            _timeOut = timeOut;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Cache key
        /// </summary>
        public string Key
        {
            get
            {
                return _key;
            }
            set
            {
                _key = value;
            }
        }

        /// <summary>
        /// Timeout in minutes
        /// </summary>
        public int TimeOut
        {
            get
            {
                return _timeOut;
            }
            set
            {
                _timeOut = value;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Add an item to the cache
        /// </summary>
        /// <param name="item">Item to add to the cache</param>
        public virtual void Add(T item)
        {
            AddToCache(_key, item, _timeOut);
        }

        /// <summary>
        /// Get an item from the cache (based on the key)
        /// </summary>
        /// <returns>Cache item</returns>
        public virtual T Get()
        {
            // try to retrieve from the cache
            return GetFromCache(_key);
        }

        /// <summary>
        /// Clear the cache
        /// </summary>
        public virtual void Clear()
        {
            ClearCache(_key);
        }

        /// <summary>
        /// Add an item to the cache
        /// </summary>
        /// <param name="key">Cache key</param>
        /// <param name="objectToAdd">Item to add</param>
        /// <param name="timeToHold">Time in minutes to hold</param>
        private static void AddToCache(string key, T objectToAdd, int timeToHold)
        {
            CacheManager cache = CacheFactory.GetCacheManager();
            cache.Add(key, objectToAdd, CacheItemPriority.Normal, null,
                          new SlidingTime(TimeSpan.FromMinutes(timeToHold)));
        }

        /// <summary>
        /// Get an item from the cache
        /// </summary>
        /// <param name="key">Cache key</param>
        /// <returns>Cache item</returns>
        private static T GetFromCache(string key)
        {
            // try to retrieve from the cache
            CacheManager cache = CacheFactory.GetCacheManager();
            return (T)cache.GetData(key);
        }

        /// <summary>
        /// Clear the cache
        /// </summary>
        /// <param name="key">Cache key</param>
        private static void ClearCache(string key)
        {
            // clear the cache
            CacheManager cache = CacheFactory.GetCacheManager();
            cache.Remove(key);
        }

        #endregion
    }

}

 

RobKraft replied on Wednesday, October 31, 2007

We use this quite a bit and it works well.  No worries. :)

Copyright (c) Marimer LLC