Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Using MemoryCache in .NET 4.0

4.63/5 (19 votes)
28 Nov 2011CPOL3 min read 239.3K  
Using MemoryCache in .NET 4.0

Since ASP.NET first came, it came up with a very powerful feature of in-memory object cache (System.Web.Caching.Cache) to store commonly used expensive data on server side. Almost every ASP.NET application/site uses this feature now. But it suffers from few shortcomings like:

  • It is available in ASP.NET only leaving WinForms or WPF clients puzzled.
  • It is not extensible to accommodate other demands to store cache objects in disk or SQL Server. However, Microsoft after realizing these shortcomings, Caching Application Block library was included in its Enterprise Library.
  • .NET developers have to look for their own mechanisms to create logical in-memory partitions called regions to group or organize cache objects in memory.

However, .NET 4.0 came up with a new set of caching APIs in System.Runtime.Caching namespace that addresses all the above shortcomings. This new namespace can be found in System.Runtime.Caching.dll assembly. But this assembly reference is available only in the target framework of .NET Framework 4, not .NET Framework 4 Client Profile.

The System.Runtime.Caching namespace contains two core sets of classes:

  • The concrete implementation of System.Runtime.Caching.MemoryCache class supports in-memory object cache. MemoryCache is closely modeled after old System.Web.Caching.Cache of ASP.NET. However, one does not have to rely upon System.Web assembly to use MemoryCache (We will see it through an example shortly).
  • Abstract types to build custom cache implementation other than in-built MemoryCache.

Now let’s move to try out an example to leverage MemoryCache features. First of all, we will create a class library type project. Add a class MyCache and assembly reference of System.Runtime.caching.dll.

MyCache Code Sample

C#
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Runtime.Caching;

namespace DotNetCachingWrapper 
{ 
public enum MyCachePriority 
{ 
Default, 
NotRemovable 
}

public class MyCache 
{ 
// Gets a reference to the default MemoryCache instance. 
private static ObjectCache cache = MemoryCache.Default; 
private CacheItemPolicy policy = null; 
private CacheEntryRemovedCallback callback = null;

public void AddToMyCache(String CacheKeyName, Object CacheItem, 
	MyCachePriority MyCacheItemPriority, List<String> FilePath) 
{ 
// 
callback = new CacheEntryRemovedCallback(this.MyCachedItemRemovedCallback); 
policy = new CacheItemPolicy(); 
policy.Priority = (MyCacheItemPriority == MyCachePriority.Default) ? 
		CacheItemPriority.Default : CacheItemPriority.NotRemovable; 
policy.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(10.00); 
policy.RemovedCallback = callback; 
policy.ChangeMonitors.Add(new HostFileChangeMonitor(FilePath));

// Add inside cache 
cache.Set(CacheKeyName, CacheItem, policy); 
}

public Object GetMyCachedItem(String CacheKeyName) 
{ 
// 
return cache[CacheKeyName] as Object; 
}

public void RemoveMyCachedItem(String CacheKeyName) 
{ 
// 
if (cache.Contains(CacheKeyName)) 
{ 
cache.Remove(CacheKeyName); 
} 
}

private void MyCachedItemRemovedCallback(CacheEntryRemovedArguments arguments) 
{ 
// Log these values from arguments list 
String strLog = String.Concat("Reason: ", arguments.RemovedReason.ToString(), " 
| Key-Name: ", arguments.CacheItem.Key, " | Value-Object: ", 
arguments.CacheItem.Value.ToString()); 
}
} 
}

Build this class library to generate assembly name of MyCachingWrapper.dll. Now we are ready to use this library in all ASP.NET web, WinForms and WPF applications.

Now let’s see examples of how this library can be commonly used in both ASP.NET Web Form and WinForm after adding reference of MyCachingWrapper.dll.

ASP.NET Page_Load

C#
protected void Page_Load(object sender, EventArgs e) 
{ 
// 
MyCache objCache = new MyCache(); 
String strUserName = objCache.GetMyCachedItem("USER_NAME") as String; 
if (String.IsNullOrEmpty(strUserName)) 
{ 
List<String> lstFiles = new List<string>(); 
lstFiles.Add(HttpContext.Current.Request.MapPath("~/XmlFiles/ListOfUsers.xml"));

XElement x = XElement.Load(HttpContext.Current.Request.MapPath
		("~/XmlFiles/ListOfUsers.xml"));
var qry = from u in x.Elements("Users") where 
		u.Element("UserCode").Value == "101" select u; 
strUserName = qry.First().Element("UserName").Value;

// Add inside cache 
objCache.AddToMyCache("USER_NAME", strUserName, MyCachePriority.Default, lstFiles); 
} 
this.lblUserName.Text = strUserName; 
}

WinForm Form_Load

C#
private void frmDefault_Load(object sender, EventArgs e) 
{ 
// 
MyCache objCache = new MyCache(); 
String strFilePath =System.IO.Path.Combine(Environment.CurrentDirectory, 
			"../../XmlFiles/ListOfUsers.xml"); 
String strUserName = objCache.GetMyCachedItem("USER_NAME") as String; 
if (String.IsNullOrEmpty(strUserName)) 
{ 
List<String> lstFiles = new List<string>(); 
lstFiles.Add(strFilePath);

XElement x = XElement.Load(strFilePath); 
var qry = from u in x.Elements("Users") where 
		u.Element("UserCode").Value == "101" select u; 
strUserName = qry.First().Element("UserName").Value;

// Add inside cache 
objCache.AddToMyCache("USER_NAME", strUserName, MyCachePriority.Default, lstFiles); 
} 
this.lblUserName.Text = strUserName; 
}

Sample ListOfUsers.xml File

XML
<?xml version="1.0"?> 
<ListOfUsers xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance 
	xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
<Users> 
<UserCode>101</UserCode> 
<UserName>Dan Brown</UserName> 
</Users> 
<Users> 
<UserCode>102</UserCode> 
<UserName>da Vinci</UserName> 
</Users> 
<Users> 
<UserCode>103</UserCode> 
<UserName>Monalisa</UserName> 
</Users> 
<Users> 
<UserCode>104</UserCode> 
<UserName>Shakespeare</UserName> 
</Users> 
<Users> 
<UserCode>105</UserCode> 
<UserName>William Wordsworth</UserName> 
</Users> 
</ListOfUsers>

Now we can debug the above code and see other interesting things. You will see the usage of MemoryCache methods and its techniques to cache objects are similar to what we have been doing in ASP.NET cache so far. Also, we see how the same MyCache library is usable in both web and windows applications.

MemoryCache Points Worth Mentioning

  • MemoryCache.Default returns the single and same instance of in-memory ObjectCache through a static read-only property.
    C#
    <p>public static MemoryCache Default { get; }</p>
  • CacheEntryRemovedCallback signature is different than what we have seen in prior versions of ASP.NET. Refer to the example. In the current callback method, one can see the following details when cache item is expired after 10 seconds.

    CallbackArgumentsDetails

  • CacheItemPriority enum in .NET 4.0 is cleaner than what we have seen in prior versions of ASP.NET. Now, it is only Default and NotRemovable.
  • In prior version of ASP.NET, CacheDependency was used to monitor changes in any underlying objects like files, SQL database tables, rows, columns, etc. Now .NET 4.0 provides ChangeMonitor class which is ASP.NET neutral and has wider scope of monitoring dependent objects to expire cache items. However, CacheDependency is still there in ASP.NET 4.0. We should use another implementation of ChangeMonitor like HostFileChangeMontor in the above example.
  • The default implementation of MemoryCache does not give us flexibility to add regions along with keys. In order to use regions, you should extend MemoryCache.

Hope all of you will appreciate the .NET 4.0 caching feature.


Filed under: .Net Technologies, ASP.Net, C#/VB.Net, CodeProject, Dot Net Tips, MemoryCache, ObjectCache Tagged: .Net 4.0, ASP.Net, C#, Caching, MemoryCache, ObjectCache, Visual Studio 2010

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)