Introduction
I am working on a WinForms application where I need to cache some information for a period of time. There are a number of ways to accomplish this. For instance, one can use the Caching Application Block from Microsoft. However, I came across a Microsoft Knowledge Base Article that covers a problem with the Caching Application Block. Inconsistencies can occur when multiple threads attempt to update the same cache item in a short time. As a workaround, the article suggests using another caching mechanism such as the ASP.NET cache.
This got me thinking. Is it possible to use the Microsoft ASP.NET cache in a WinForms application? So, I wrote a sample app to see if it is possible.
The Cache Object
To clarify what I mean by ASP.NET cache, I am referring specifically to the System.Web.Caching.Cache
object. I am not referring to page cache, application cache, etc. In my sample, I want to see if the Cache
object itself can be used in a .NET WinForms application.
Getting Started
I start by creating a new WinForms application in Visual Studio .NET. As I always do, I deleted the Form1.cs created by VS.NET and I added a new class called AppMain.cs. I prefer using AppMain
to define the application entry point instead of defining the entry point as part of a form. Next, I add the System.Web.dll to my project reference list. This assembly is needed to reference the Cache
object.
With the AppMain
class in place, I added code to start the HttpRuntime
. The HttpRuntime
class provides a set of ASP.NET run-time services to the current application. This includes the Cache
object that is used in this sample. And because I am only interested in accessing the Cache
object within my sample application, I will expose a public property from the AppMain
class called Cache
. This allows other classes within my application to access the cache. Here is the code for AppMain.cs:
using System;
using System.Threading;
using System.Web;
using System.Web.Caching;
using System.Windows.Forms;
namespace CacheSample
{
public class AppMain
{
private static HttpRuntime _httpRuntime;
public static Cache Cache
{
get
{
EnsureHttpRuntime();
return HttpRuntime.Cache;
}
}
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private static void EnsureHttpRuntime()
{
if( null == _httpRuntime )
{
try
{
Monitor.Enter( typeof( AppMain ) );
if( null == _httpRuntime )
{
_httpRuntime = new HttpRuntime();
}
}
finally
{
Monitor.Exit( typeof( AppMain ) );
}
}
}
}
}
Using the Cache
Since this is nothing more than a sample application, I will keep the requirements simple. The application will include a single Form. On this form, the user can enter any text to be inserted into the cache. There is also a button to retrieve the value from the cache. A default message is displayed if the cache is empty.
Here is the code for the form:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Web.Caching;
using System.Data;
namespace CacheSample
{
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox txtValueToPutInCache;
private System.Windows.Forms.TextBox txtValueInCache;
private System.Windows.Forms.Button btnPutInCache;
private System.Windows.Forms.Button btnGetFromButton;
private const string CACHE_KEY = "APPCACHEKEY";
private System.ComponentModel.Container components = null;
public Form1()
{
InitializeComponent();
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.txtValueToPutInCache = new System.Windows.Forms.TextBox();
this.txtValueInCache = new System.Windows.Forms.TextBox();
this.btnPutInCache = new System.Windows.Forms.Button();
this.btnGetFromButton = new System.Windows.Forms.Button();
this.SuspendLayout();
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(8, 16);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(113, 16);
this.label1.TabIndex = 0;
this.label1.Text = "Value to put in cache:";
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(8, 40);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(95, 16);
this.label2.TabIndex = 1;
this.label2.Text = "Value from cache:";
this.txtValueToPutInCache.Location = new System.Drawing.Point(128, 16);
this.txtValueToPutInCache.Name = "txtValueToPutInCache";
this.txtValueToPutInCache.Size = new System.Drawing.Size(200, 20);
this.txtValueToPutInCache.TabIndex = 2;
this.txtValueToPutInCache.Text = "";
this.txtValueInCache.Location = new System.Drawing.Point(128, 40);
this.txtValueInCache.Name = "txtValueInCache";
this.txtValueInCache.ReadOnly = true;
this.txtValueInCache.Size = new System.Drawing.Size(200, 20);
this.txtValueInCache.TabIndex = 3;
this.txtValueInCache.Text = "";
this.btnPutInCache.Location = new System.Drawing.Point(352, 16);
this.btnPutInCache.Name = "btnPutInCache";
this.btnPutInCache.Size = new System.Drawing.Size(104, 23);
this.btnPutInCache.TabIndex = 4;
this.btnPutInCache.Text = "Put in Cache";
this.btnPutInCache.Click +=
new System.EventHandler(this.btnPutInCache_Click);
this.btnGetFromButton.Location = new System.Drawing.Point(352, 40);
this.btnGetFromButton.Name = "btnGetFromButton";
this.btnGetFromButton.Size = new System.Drawing.Size(104, 23);
this.btnGetFromButton.TabIndex = 5;
this.btnGetFromButton.Text = "Get from Cache";
this.btnGetFromButton.Click +=
new System.EventHandler(this.btnGetFromButton_Click);
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(488, 133);
this.Controls.Add(this.btnGetFromButton);
this.Controls.Add(this.btnPutInCache);
this.Controls.Add(this.txtValueInCache);
this.Controls.Add(this.txtValueToPutInCache);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
#endregion
private void btnPutInCache_Click(object sender, System.EventArgs e)
{
AppMain.Cache.Insert(
CACHE_KEY,
txtValueToPutInCache.Text,
null,
Cache.NoAbsoluteExpiration,
TimeSpan.FromSeconds( 60 ) );
}
private void btnGetFromButton_Click(object sender, System.EventArgs e)
{
string value;
value = AppMain.Cache[ CACHE_KEY ] as string;
if( null == value )
{
value = "[No value in the cache.]";
}
txtValueInCache.Text = value;
}
}
}
Conclusion
As you can see from this code, once the application starts the HttpRuntime
, the Cache
object can be used. As this sample shows, not only can ASP.NET applications use the Cache
object but WinForms applications can take advantage of the rich features provided by this .NET Framework class.
References