In our code we have an MVC controller action that is decorated with the OutputCacheAttribute. Is there any way in some other action to clear the cache for the first action?
2 Answers
It depends. If it is a child action the cache is stored in the MemoryCache and the only way to clear it is undocumented and involves busting the whole memory cache:
OutputCacheAttribute.ChildActionCache = new MemoryCache("NewDefault");
The drawback of course is that this removes all cached child actions and not just the cached output of this child action. If it is a normal action then you could use the Response.RemoveOutputCacheItem method by passing it the url of the action that was cached. You might also find the following article interesting.
Caching in ASP.NET MVC 3 has still a very long way to go. Hopefully they are improving many things in ASP.NET MVC 4 and simplifying it.
1 Comment
Yes, it is possible I have found the answer in this book. See 14.3 Output Cache In ASP.NET MVC page 372 - Deterministically removing items from cache
When we decorate an action with the OutputCacheAttribute , OutputCache stores its result into ASP.NET Cache and automatically recovers it when it must serve a subse- quent analogous request. If we knew which cache key the page belongs to, we could easily remove it. Unfortunately, this isn’t easily possible, and even if it were, we aren’t supposed to know it because it resides in the internal logic of the caching infrastruc- ture and might change without notice in future releases. What we can do, though, is leverage the cache dependencies mechanism to achieve a similar result. This feature is similar to the change monitors we’re going to talk about in section 14.4. Leveraging cache dependencies consists of tying one cache entry to another to automatically remove the first one when the latter is invalidated.
And a piece of code
public class DependencyOutputCacheAttribute : OutputCacheAttribute
{
public string ParameterName { get; set; }
public string BasePrefix { get; set; }
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
base.OnResultExecuting( filterContext );
string key = string.IsNullOrEmpty( BasePrefix )
? filterContext.RouteData.Values["action"] + "_" + filterContext.RouteData.Values["controller"]
: BasePrefix;
key = AddToCache( filterContext, key, ParameterName);
}
private string AddToCache(ResultExecutingContext filterContext, string key, string parameter)
{
if ( !string.IsNullOrEmpty( parameter ) && filterContext.RouteData.Values[parameter] != null) {
key += "/" + filterContext.RouteData.Values[parameter];
filterContext.HttpContext.Cache.AddBig( key, key );
filterContext.HttpContext.Response.AddCacheItemDependency( key );
}
return key;
}
}
And Remove cache dependency attribute
public class RemoveCachedAttribute : ActionFilterAttribute
{
public string ParameterName { get; set; }
public string BasePrefix { get; set; }
public override void OnResultExecuting( ResultExecutingContext filterContext)
{
base.OnResultExecuting(filterContext);
string key = string.IsNullOrEmpty(BasePrefix) ?
filterContext.RouteData.Values["action"].ToString() + "_" +
filterContext.RouteData.Values["controller"].ToString() : BasePrefix;
if (!string.IsNullOrEmpty(ParameterName))
key += filterContext.RouteData.Values[ParameterName];
filterContext.HttpContext.Cache.Remove(key);
}
}
and finally use it
[DependencyCache( BasePrefix = "Story", ParameterName = "id" )]
public virtual ActionResult ViewStory(int id){
//load story here
}
[HttpPost, RemoveCache( BasePrefix = "Story", ParameterName = "id" )]
public virtual ActionResult DeleteStory(int id){
//submit updated story version
}
[HttpPost, RemoveCache( BasePrefix = "Story", ParameterName = "id" )]
public virtual ActionResult EditStory(Story txt){
//submit updated story version
}
where
public class Story {
int id {get;set;} //case is important
string storyContent{get;set;}
}