This post is based on my previous post about caching versus request reduction.
Okay, building a
self-managed caching system on top of MVC (3+) and Razor. Frankly, this makes
me miss WebForms since some things are harder in Razor.
So the workflow is
as follows: On initial request the server checks a cookie to see whether to
include a full section or whether to include a stub.
@if
(ClientCache.OnClient("trial"))
{
<clientcache
cacheid="trial"></clientcache>
} else
{
<cacheable cacheid="trial">
<div >
… content …
</div>
</cacheable>
}
A couple things of
note. I am using custom HTML tags. In this case I think it is a technique that
makes sense. I am using a client cache variable on the WebViewPage. I created
this by descending from WebViewPage. Here is the very boring class:
public
abstract class ClientCachingWebViewPage<TModel> :
WebViewPage<TModel>
{
private ClientCache _clientCache;
public ClientCache ClientCache
{
get { return _clientCache ?? (_clientCache = new ClientCache()); }
}
}
*yawn* So the server
determines whether the client has cached the section by checking the cookie.
Here is the object that does the checking:
public class
ClientCache
{
private string[] _cacheList;
public const string CacheCookieName = "ClientCache";
public const char Divider = ':';
public string[] ClientCacheIds
{
get
{
if (_cacheList == null)
{
HttpCookie cookie =
HttpContext.Current.Request.Cookies[CacheCookieName];
_cacheList = cookie != null
? cookie.Value.Split(new[] { Divider })
: new string[0];
}
return _cacheList;
}
}
public bool OnClient(string id)
{
return ClientCacheIds.Contains(id);
}
}
Okay, so that all
there is on the server. A check on the cookie and a conditional that includes a
stub. On the client the code is as follows:
// get all
cacheable sections and store in web storage
$("cacheable").each(function
() {
var key = $(this).attr("cacheId");
var value = $(this).html();
localStorage.setItem(key, value);
var myCookie = $.cookie("ClientCache");
myCookie = myCookie ? myCookie + ":" + key : key;
$.cookie("clientCache", myCookie, { expires: 365 });
});
// update all
stubs with cached values
$("clientcache").each(function
() {
var key = $(this).attr("cacheId");
var value = localStorage.getItem(key);
$(this).html(value);
});
Note that I am using
Jquery and a Jquery cookie plugin. In a real implementation you might not want
to do this because then you can't cache these libraries with this technique.
So this works pretty
well for a first attempt. It also seems like you could easily build a system
that loaded cacheable sections for later pages after a page has loaded. I will
maybe do that later. But there are a couple issues. One is that you can't cache
a volatile section, well you can, but it will not work correctly. This is built
with the notion that your markup or templates are going to be separate from
your volatile data.
The other issue that
really bothers me is the markup in the cshtml file. I hate how it looks. I
would much rather something like:
@ClientCache("cacheid")
{
...content…
}
So
doing this with WebForms would be easy, but with Razor it isn't so clear. You
could do something like RenderSection or Html.Partial
but this would move the markup out of the page, but maybe that isn't so
onerous. Another is to use Razor templates although I worry that would cause
references in the Razor to work badly.
This whole system
also falls to pieces if you clear local storage without updating the cookie so
you need a system that scans that scans the web storage for the ids it thinks
it has and if needed does another request to get the sections that are missing.
This can tie in to a mechanism that does the predictive caching.
No comments:
Post a Comment