<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://devlicio.us/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Alan Northam : HttpRuntime.Cache</title><link>http://devlicio.us/blogs/alan_northam/archive/tags/HttpRuntime.Cache/default.aspx</link><description>Tags: HttpRuntime.Cache</description><dc:language>en</dc:language><generator>CommunityServer 2008.5 SP1 (Build: 31106.3070)</generator><item><title>Practical Implementation - spending some cache.</title><link>http://devlicio.us/blogs/alan_northam/archive/2008/03/17/practical-implementation-spending-some-cache.aspx</link><pubDate>Tue, 18 Mar 2008 01:35:00 GMT</pubDate><guid isPermaLink="false">40756a8b-6212-4073-9d98-6c26781577de:39697</guid><dc:creator>anortham</dc:creator><slash:comments>1</slash:comments><description>&lt;p&gt;Take a look at my &lt;a href="http://devlicio.us/blogs/alan_northam/archive/2008/03/06/cold-hard-cache.aspx" target="_blank"&gt;previous post&lt;/a&gt; for the details on a cache helper method that I like to use.&amp;nbsp; In this post, we&amp;#39;ll be putting it to work (the word for today is J - O - B).&amp;nbsp; Here&amp;#39;s a look at the code for reference.&lt;/p&gt;
&lt;p&gt;
using System;
using System.Reflection;
using System.Web;
using System.Web.Caching;
using log4net;

namespace CacheManagement
{
    public delegate bool Factory&amp;lt;T&amp;gt;( out T instance );
    public class CacheHelper
    {
        private static readonly ILog log =
           LogManager.GetLogger(
               MethodBase.GetCurrentMethod().DeclaringType );

        private static readonly Cache cache = HttpRuntime.Cache;
        private CacheHelper() { }

        public static T GetFromCache&amp;lt;T&amp;gt;( string key, int cacheTimeInMinutes, Factory&amp;lt;T&amp;gt; retrieveMethod ) where T : class
        {
            T target = cache[key] as T;
            if ( target == null )
            {
                log.Info( &amp;quot;Cache miss for key:&amp;quot; + key + &amp;quot;  type:&amp;quot; + typeof( T ) );
                if ( retrieveMethod( out target ) )
                {
                    cache.Insert( key, target, null, DateTime.Now.AddMinutes( cacheTimeInMinutes ),
                        Cache.NoSlidingExpiration, CacheItemPriority.Normal, OnRemove );
                }
            }
            else
            {
                log.Info( &amp;quot;Cache hit for key:&amp;quot; + key + &amp;quot;  type:&amp;quot; + typeof( T ) );
            }
            return target;
        }

        public static void OnRemove( string key, object cacheItem, CacheItemRemovedReason reason )
        {
            log.Info( &amp;quot;Object removed from cache: Key-&amp;quot; + key + &amp;quot;: Reason-&amp;quot; + reason );
        }
    }
}
&lt;/p&gt;
&lt;p&gt;
Enter the &lt;a href="http://asp.net/AJAX/AjaxControlToolkit/Samples/AutoComplete/AutoComplete.aspx" target="_blank"&gt;ASP.NET AJAX AutoComplete Extender&lt;/a&gt;.&amp;nbsp; It&amp;#39;s a perfect candidate for a little cache help.&amp;nbsp; I certainly wouldn&amp;#39;t want the database being hit every time a user typed the next letter in the autocomplete box.&amp;nbsp; I apologize for wearing you down with all this talk.&amp;nbsp; I think I&amp;#39;ve said too much.&amp;nbsp; Let&amp;#39;s look at the code for autocomplete webservice.&lt;/p&gt;
&lt;p&gt;
using log4net;
using System.Reflection;
using System.Web.Services;
using System.Collections.Generic;
using CacheManagement;

[WebService( Namespace = &amp;quot;http://tempuri.org/&amp;quot; )]
[WebServiceBinding( ConformsTo = WsiProfiles.BasicProfile1_1 )]
[System.Web.Script.Services.ScriptService]
public class AutoCompleteWebService : WebService
{
    private static readonly ILog log =
       LogManager.GetLogger(
           MethodBase.GetCurrentMethod().DeclaringType );

    private static readonly int cacheTTL = 15;
    private static readonly int minimumPrefixLength = 3;

    public AutoCompleteWebService()
    {

        //Uncomment the following line if using designed components 
        //InitializeComponent(); 
    }

    [WebMethod]
    public string[] GetWordList( string prefixText, int count )
    {
        //minimumPrefixLength should never be greater than prefixText.Length
        //this is a sanity check to insure the minimumPrefixLength field was set corretly
        string prefixKey = prefixText.Substring( 0,
            minimumPrefixLength &amp;gt; prefixText.Length
                ? prefixText.Length : minimumPrefixLength );

        //get the working list
        List&amp;lt;string&amp;gt; searchList = CacheHelper.GetFromCache&amp;lt;List&amp;lt;string&amp;gt;&amp;gt;(
            prefixKey,
            cacheTTL,
            delegate( out List&amp;lt;string&amp;gt; instance )
            {
                return DataAccess.GetWordList( prefixKey, out instance );
            } );

        log.Debug( &amp;quot;searchList full count: &amp;quot; + searchList.Count );

        //narrow the results if the prefixText is longer than prefixKey
        if ( prefixText.Length &amp;gt; prefixKey.Length )
        {
            searchList = searchList.FindAll(
                delegate( string s )
                {
                    return s.Substring( 0, prefixText.Length &amp;gt; s.Length
                        ? s.Length : prefixText.Length ) == prefixText;
                } );
        }

        //only return at maximum the number of results requested by count
        if ( searchList.Count &amp;gt; count )
            searchList = searchList.GetRange( 0, count );

        return searchList.ToArray();
    }
}
&lt;/p&gt;
&lt;p&gt;The webservice takes the first part of the prefixText (minimumPrefixLength should match the value in the autocomplete extender control) and queries the database for all matches.&amp;nbsp; It stores that result set in cache and then filters the list by prefixText if needed.&amp;nbsp; Finally it will take the top X (count is specified by the control) and return it to the control.&lt;/p&gt;
&lt;p&gt;If a user was searching for &amp;quot;automobile&amp;quot;, after typing a-u-t all possible results would be cache so that each successive letter will not hit the database.&amp;nbsp; Another database hit will be incurred when a user searches for a different 3 letter prefix.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I&amp;#39;m attaching the full sample code along with a Sql Express database that contains some huge word list I found online (to be attached later, technical difficulties).&amp;nbsp; Enjoy and try not to spend it all in one place.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://devlicio.us/aggbug.aspx?PostID=39697" width="1" height="1"&gt;</description><category domain="http://devlicio.us/blogs/alan_northam/archive/tags/Featured/default.aspx">Featured</category><category domain="http://devlicio.us/blogs/alan_northam/archive/tags/Cache/default.aspx">Cache</category><category domain="http://devlicio.us/blogs/alan_northam/archive/tags/AJAX/default.aspx">AJAX</category><category domain="http://devlicio.us/blogs/alan_northam/archive/tags/AutoComplete/default.aspx">AutoComplete</category><category domain="http://devlicio.us/blogs/alan_northam/archive/tags/HttpRuntime.Cache/default.aspx">HttpRuntime.Cache</category></item><item><title>Cold, Hard, Cache!</title><link>http://devlicio.us/blogs/alan_northam/archive/2008/03/06/cold-hard-cache.aspx</link><pubDate>Fri, 07 Mar 2008 04:04:00 GMT</pubDate><guid isPermaLink="false">40756a8b-6212-4073-9d98-6c26781577de:39576</guid><dc:creator>anortham</dc:creator><slash:comments>9</slash:comments><description>&lt;p&gt;Don&amp;#39;t get me wrong, I really like the Asp.Net Cache (or HttpRuntime.Cache or Uncle Daddy if you want to call it that... you&amp;#39;re a little odd aren&amp;#39;t you?), but sometimes it just can&amp;#39;t be trusted.&amp;nbsp; I mean, I just gave you (the cache) my precious object a second ago and now you claim you don&amp;#39;t have it?&amp;nbsp; Did you lose it?&amp;nbsp; Did you sell it on eBay?&amp;nbsp; Did you pawn it to support your habit?&amp;nbsp; Please at least tell me you got a decent price for it.&amp;nbsp; &lt;/p&gt;
&lt;p&gt;One of the great things about the cache is also what makes it so untrustworthy. &amp;nbsp; Objects stored there are referenced with &lt;a href="http://msdn2.microsoft.com/en-us/library/system.weakreference.aspx" target="_blank"&gt;WeakReference&lt;/a&gt; so if your sever starts getting low on memory GC will collect objects from the cache to free some up.&amp;nbsp; Also take into account that if you put an object in Cache and set it to expire after X minutes, when you go back to retreive it, do you really know how long it&amp;#39;s been?&lt;/p&gt;
&lt;p&gt;So what we want is a way to tell the cache &amp;quot;I want this specific object (a yellow Tonka dumptruck, metal, not plastic) and if you don&amp;#39;t have it, here are instructions on how to get it&amp;quot;.&lt;/p&gt;
&lt;p&gt;Something like this:&lt;/p&gt;
&lt;p&gt;

using System;
using System.Reflection;
using System.Web;
using System.Web.Caching;
using log4net;
public delegate bool Factory&amp;lt;T&amp;gt;( out T instance );
public class CacheHelper
{
    private static readonly ILog log =
        LogManager.GetLogger(
            MethodBase.GetCurrentMethod().DeclaringType );

    private static readonly Cache cache = HttpRuntime.Cache;
    private CacheHelper(){}

    public static T GetFromCache&amp;lt;T&amp;gt;(string key, int cacheTimeInMinutes, Factory&amp;lt;T&amp;gt; retrieveMethod) where T:class
    {
        T target = cache[key] as T;
        if(target == null)
        {
            log.Info( &amp;quot;Cache miss for key:&amp;quot; + key + &amp;quot;  type:&amp;quot; + typeof(T));
            if(retrieveMethod(out target))
            {
                cache.Insert( key, target, null, DateTime.Now.AddMinutes( cacheTimeInMinutes ),
                    Cache.NoSlidingExpiration, CacheItemPriority.Normal, OnRemove );
            }            
        }
        else
        {
            log.Info( &amp;quot;Cache hit for key:&amp;quot; + key + &amp;quot;  type:&amp;quot; + typeof(T));
        }
        return target;
    }

    public static void OnRemove( string key, object cacheItem, CacheItemRemovedReason reason )
    {
        log.Info( &amp;quot;Object removed from cache: Key-&amp;quot; + key + &amp;quot;: Reason-&amp;quot; + reason);
    }

    public static void RemoveFromCache(string key)
    {
        cache.Remove( key );
    }
}&lt;/p&gt;
&lt;p&gt;
Usage would look like this:
&lt;/p&gt;
&lt;p&gt;
List&amp;lt;string&amp;gt; searchList = CacheHelper.GetFromCache&amp;lt;List&amp;lt;string&amp;gt;&amp;gt;(
            key,
            5,
            delegate( out List&amp;lt;string&amp;gt; instance ) { return SomeClass.GetSearchList( key, out instance ); } );
&lt;/p&gt;
&lt;p&gt;
I call it the code equivalent of &amp;quot;Trust but verify&amp;quot;.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://devlicio.us/aggbug.aspx?PostID=39576" width="1" height="1"&gt;</description><category domain="http://devlicio.us/blogs/alan_northam/archive/tags/C_2300_/default.aspx">C#</category><category domain="http://devlicio.us/blogs/alan_northam/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://devlicio.us/blogs/alan_northam/archive/tags/Cache/default.aspx">Cache</category><category domain="http://devlicio.us/blogs/alan_northam/archive/tags/HttpRuntime.Cache/default.aspx">HttpRuntime.Cache</category></item></channel></rss>