📜 ⬆️ ⬇️

Alternative way to cache UserControls in Asp.net

I think everyone who uses Asp.net to develop web sites is well aware that Asp.net has built-in UserControls caching.
Any user element can be cached for a specific time depending on various conditions. Such a cache works extremely quickly and in most cases this is quite enough, but in projects in which I participate, this was not enough.
The main disadvantages were as follows:

Initially, we periodically generated the cache for the entire site, but over time this became a very big problem due to the complexity of the controls, so I decided to look for another solution.

Its essence is to create a control that would know how to cache itself.
To do this, take the UserControl class and create the CacheUserControl class derived from it:
public abstract class CacheUserControl : UserControl
{
public abstract string GetCachePath();
public abstract TimeSpan GetCacheTime();

public bool IsCache { get ; protected set ; }

protected string cacheText;
protected string cachePath;
protected sealed override void OnInit( EventArgs e)
{
...
}
public sealed override void RenderControl(HtmlTextWriter htmlTextWriter)
{
...
}
}



As you can see, there are two abstract methods.
The GetCachePath method returns the path to the cache file. This path should reflect all dependencies of the control so that it can select the correct file.
The GetCacheTime method returns the time interval through which the cache will be considered obsolete and will be updated if necessary.

Next, you need to override two methods of the UserControl class:
')
The OnInit method should check if the cache file exists, and if so, the control should delete all its child controls so that they do not trigger any of their events and load the cache from the file:

protected sealed override void OnInit( EventArgs e)
{
try
{
cachePath = GetCachePath();
if (cachePath != null )
{
var cacheFile = new FileInfo(cachePath);
if (cacheFile.Exists)
{
var cacheTime = GetCacheTime();

var cacheTimeExpired = cacheFile.CreationTime + cacheTime < DateTime .Now;
if (!cacheTimeExpired)
{
using ( var reader = cacheFile.OpenText())
{
cacheText = reader.ReadToEnd();
IsCache = true ;
Controls.Clear();
}
}
}
}
}
catch (IOException)
{
}

base .OnInit(e);
}


The RenderControl method should check whether the cache is loaded, and if it is loaded, then render it, and if not, render the control in normal mode and additionally create a cache file:

public sealed override void RenderControl(HtmlTextWriter htmlTextWriter)
{
if (IsCache)
{
htmlTextWriter.Write(cacheText);
}
else
{
base .RenderControl(htmlTextWriter);

try
{
if (cachePath != null )
{
using ( var streamWriter = new StreamWriter(cachePath, false , Encoding .UTF8))
{
using (htmlTextWriter = new HtmlTextWriter(streamWriter))
{
base .RenderControl(htmlTextWriter);
}
}
}
}
catch (IOException)
{
}
}
}


As a result, to use self-caching control, you need to change the base class from UserControl to CacheUserConrtol and additionally define two methods, for example:

public override string GetCachePath()
{
var id = Request.QueryString[ "id" ];
if ( string .IsNullOrEmpty(id)) return null ;

var currency = "rur" ;
var cookie = Request.Cookies[ "currency" ];
if (cookie != null )
{
var value = cookie.Value;
if ( value == "usd" ) currency = "usd" ;
else if ( value == "euro" ) currency = "euro" ;
}

var path = string .Format( "~/cache/cat_{0}_cur_{1}.html" , id, currency);
return Server.MapPath(path);
}
public override TimeSpan GetCacheTime()
{
return TimeSpan .FromDays(7);
}


Now the control itself will create separate cache files for different categories (determined by id) for each currency of the directory (determined from cookie)

The only disadvantage of this approach is that over time, garbage will accumulate in the cache, so it will periodically have to be completely cleared. It should also be noted that the Page_Load method of such a control is invoked in any case, but the events of the child controls only if the content is not taken from the file.

Well, that's all.

Source: https://habr.com/ru/post/60352/


All Articles