TURKISH BLOG   |  ABOUT ME  |  ARCHIVES  | DELETE LANGUAGE COOKIE

Enes TAYLAN

Mind Hegemony - Mood 1.0 - Total Control Edition

Using Cache and Purging it with IDictionaryEnumerator

clock November 22, 2009 07:40

Using cache can provide enormous performance benefits. To get them, however, we, as developers, should manage cache in a good, determined way. General way of cache management is:

1. Determine keys (will be represented in string) to each information group (example: for category domain objects use category, for a specific category use category_{id}). Use this key everywhere in your code, don't change (don't use category somewhere and Category in another place), you can constant string variables for this purpose.

2. When you get data first check whether it is in cache already; if yes, take it else get from your datasource then immediately add that data to the cache with the key.

3. Write a global method that takes a prefix, to purge cache items has the prefix

As an example: Below the code method that takes data from datasource (never mind its domain, just focus on its purpose):

 public static int GetIhalelerCount()
{    
    int articleCount = 0;

    //construct your key
    string key = "tenders_approvedCount_" + categoryID.ToString();

    //check whether it is on cache or not, if yes, take it
    if (BaseDomain.Settings.EnableCaching && BizObject.Cache[key] != null)
    {
        articleCount = (int)BizObject.Cache[key];
    }

    //at this point, it is not in the cache, get it from datasource
    //and insert it to the cache with your predetermined key
    else
    {
        articleCount = SiteProvider.Provider.GetApprovedIhalelerCountByCategory(categoryID);
        BaseDomain.CacheData(key, articleCount);
    }
    return articleCount;
}

The CacheData method:

protected static void CacheData(string key, object data)
{
    //checks whether data is null or not, because in the case
    //null exception is thrown
    if (data != null)
    {	           
        HttpContext.Current.Cache.Insert(key, data, null,
            DateTime.Now.AddSeconds(10), TimeSpan.Zero);
    }
}
We need also a PurgeCacheItems method to clean unnecessary data. This method is generally used in Delete, Update, Insert methods because after this modifications data at hand become obsolete.

protected static void PurgeCacheItems(string prefix)
{
    prefix = prefix.ToLower();
    List itemsToRemove = new List();

    IDictionaryEnumerator enumerator = HttpContext.Current.Cache.GetEnumerator();
    while (enumerator.MoveNext())
    {
        if (enumerator.Key.ToString().ToLower().StartsWith(prefix))
            itemsToRemove.Add(enumerator.Key.ToString());
    }

    foreach (string item in itemsToRemove)
    {
        HttpContext.Current.Cache.Remove(item);
    }
}

In PurgeCacheItems, to get cache's content take enumeration of it by using GetEnumerator() method and iterate through it. You can also use itemsToRemove list above to list in an .aspx page, like:

protected void Page_Load(object sender, EventArgs e)
{
    IDictionaryEnumerator enumerator = HttpContext.Current.Cache.GetEnumerator();    

    while (enumerator.MoveNext())
    {
        Label1.Text += enumerator.Key.ToString() + "
"; Label2.Text += enumerator.Value.ToString() + "
"; } }

Hope, this helps.



CACHING WITH SQL DEPENDENCY SUPPORT-1

clock July 17, 2009 02:40

Caching data that comes from an SQL database (in other words dependencies to database tables) in RAM has been supported since ASP.NET 2.0. In practice this means, we can cache the data for undetermined period until the source database table is updated. This cache mechanism works for SQL Server 7 and later including SQL 2005 and SQL 2008. SQL 2005 and SQL 2008 supports another type of cache invalidation based on events. I call first mechanism as Table Level SQL Dependencies and second as Command Level SQL Dependencies. In this article, "Table Level SQL Dependencies" will be spoken. For "Command Level SQL Dependencies" you can look at "Caching with SQL Dependency Support-2".

Table Level SQL Dependencies
This cache invalidation mechanism use polling technique and creates a table AspNet_CacheTablesForChangeNotification for the database in which you want to enable cache invalidation and creates a row for each table, you customly select these ones, in this database. ASP.NET runtime engine places a counter in these rows and checks every so often (the interval is configurable) and if it is updated than deletes the cached information from the cache. To create required database schema run aspnet_regsql.exe run below command:

aspnet-regsql.exe -E -S .\YourCustomSqlServerNameHere -d YourCustomDatabaseNameHere -ed
 -E option is for Windows Integrated Security
 -S for Sql Server Instance name
 -d for database name
 -ed for "enable database"

Above command creates required tables, triggers, stored procedures etc. The next step is adding a row to this table for the table to which you're adding support  (this also automatically adds a trigger to your table)

aspnet-regsql.exe -E -S .\YourCustomSqlServerNameHere -d YourCustomDatabaseNameHere -t Customers -et
 -t is for table name
 -et is for "enable table"

The last thing to enable Sql Cache dependency is to configure web.config:

<configuration>
 <connectionStrings>
  <add name="MyConString"............ //other details here    />
 </connectionStrings>
 
 <system.web>
  <caching>
   <sqlCacheDependency enabled="true" pollTime="20000">
    <databases>
     <add name="MyCustomCache " connectionStringName="MyConString" pollTime="5000" />
    </databases> 
   </sqlCacheDependency>
  </caching>
 </system.web>
</configuration>


pollTime above defines the polling interval of 20000 milliseconds (20 seconds) default and re-writes it for our specific database by 5000 milliseconds (5 seconds) Now everything is configured so let’s try code:

public List GetProducts()
{
     List productNames = null;
     if (Cache["ProductNames"] != null) 
     {
         productNames = (List)Cache["ProductNames"];
         //if cache already contains the info return it
     }
     else
     {
         using (SqlConnection conn = new SqlConnection(_connString))
         {
             SqlCommand cmd = new SqlCommand("SELECT ProductNames FROM Products", cn);
             productNames = cmd.ExecuteReader().FillListFromReader(cmd.ExecuteReader());

             //FillListFromReader is an umimplemented method that takes the
             //the result of "ExecuteReader" and converts it to List

             SqlCacheDependency dep = new SqlCacheDependency("MyCustomCache", "Products");
             //MyCustomCache is the name we defined in web.config
             //Products is the table name we want to cache on it

             Cache.Insert("ProductName", productNames, dep);
         }
     }
}


HOW TO USE CACHE AND CACHE DEPENDENCY ?

clock June 21, 2009 04:28

Using cache is an easy way to increase efficiency in programs. By using cache techniques we can store the data in RAM that is normally in harddisk or another stuff whose access time is much bigger than RAM's. In this article, I will write a method that gets the subdirectory names in a directory and return them to caller. ( this method can be used, for example, to populate theme names in a dropdownlist so that user can select her preferred style. .NET cache classes are in System.Web.Cache hence don't forget import it. ( by using statement ) Another important thing about cache is: how much time the info should reside in cache? Because if it losses its validity after some time, it is just a garbage and may decrease the efficiency. If cache information is deleted earlier than it normally would be, we can't rely on the cache. Therefore, to solve this problem we have CacheDependency objects that enable us bind info to a file in system file hierarchy and say: "When this file is updated then delete this specific cache information. Now see the code:

 	public static string[] GetThemes()
        {
            if (HttpContext.Current.Cache["SiteThemes"] != null)
            {
		    //if there is already cache info then just use it
                return (string[])HttpContext.Current.Cache["SiteThemes"];
            }

            else //when cache is empty
            {
                string themesDirPath = HttpContext.Current.Server.MapPath("~/App_Themes");
		    //map virtual file location to full one

                string[] themes = Directory.GetDirectories(themesDirPath);
		    //using System.IO features get all directories in a specific one

                for (int i = 0; i < themes.Length; i++)
                {
                    themes[i] = Path.GetFileName(themes[i]);
                }

                CacheDependency dep = new CacheDependency(themesDirPath);
		    //define a dependency on the directory "themesDirPath"

                HttpContext.Current.Cache.Insert("SiteThemes", themes, dep);    
		    //Here we insert the "themes" string array with the cache key
		    //"SiteThemes" and say that if "dep" dependency object
		    //warns you delete this key - value pair from cache

		    
                return themes;
            }
        }

Here information is stored as key - value pairs. ( key is "SiteThemes" string and the value is "themes" array ) In this code snippet, when directory "App_Themes" is updated then "SiteThemes"-"themes" key - value pair also deleted. What we benefit from this code is: we don't read content of "App_Themes" every time (means many IO costs) instead read it only when the method is first invoked.