我有一些控制器操作,我希望自定义缓存。例如,假设我有一个
控制器动作ActionResult Index(string name) {}
。我想在服务器上缓存此操作的HTML,除非url中有“ live = true ”查询字符串参数。如果该参数存在,我想从服务器缓存中删除该操作结果并正常提供响应。
我们通常使用OutputCache(Location=OutputCacheLocation.Server)
属性来执行缓存。如果URL中存在 live = true 参数,是否可以以某种方式扩展此属性并清除缓存?
如果我无法自定义OutputCache属性以获取我需要的行为,是否可以使用其他方法来实现此目的?
更新
基于詹姆斯的反馈,这里是我的代码:
public class LiveOutputCacheAttribute : OutputCacheAttribute
{
private const string _resetParam = "live";
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var context = filterContext.HttpContext;
AddLiveToVaryByParam();
if (context.Request[_resetParam] == "true")
{
var urlToRemove = GetUrlToRemove(filterContext);
context.Response.RemoveOutputCacheItem(urlToRemove);
return;
}
base.OnActionExecuting(filterContext);
}
private void AddLiveToVaryByParam()
{
// add live reset flag when vary by param is specified
if (VaryByParam != "*" && !VaryByParam.Contains("live"))
VaryByParam = string.Format("{0};{1}",VaryByParam, _resetParam).TrimStart(';');
}
private static string GetUrlToRemove(ActionExecutingContext filterContext)
{
var routeValues = new RouteValueDictionary(filterContext.ActionParameters);
var urlHelper = new UrlHelper(filterContext.RequestContext);
string action = filterContext.ActionDescriptor.ActionName;
string controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
return urlHelper.Action(action, controller, routeValues);
}
}
以下是我在行动中使用此方法的方法:
[LiveOutputCache(Location = OutputCacheLocation.Server, Duration = 60 * 60, VaryByParam = "name")]
public ActionResult Index(string name)
{
ViewData.Model = name + "-----" + DateTime.Now.Ticks.ToString();
return View();
}
问题是当我使用live = true参数时,它仍然没有从缓存中删除原始请求。我在这里做错了吗?
答案 0 :(得分:3)
您可以使用VaryByParam属性来检查实时选项是否为真,例如
public class LiveOutputCacheAttribute : OutputCacheAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (VaryByParam == "true")
{
// clear cache
return;
}
base.OnActionExecuting(filterContext);
}
}
...
[LiveOutputCache(Location=OutputCacheLocation.Server, VaryByParam="live")]
public ActionResult Index(string name)
{
...
}
请参阅How to programmatically clear outputcache for controller action method了解清除部分。
答案 1 :(得分:0)
您无法自定义OutputCacheAttribute以获取该行为,但您可以编写自己的CustomCacheAttribute来实现此目的。为此,您可以获取OutputCacheAttribute的源代码(MVC是opensource,因此您可以执行此操作),复制它并重写函数OnActionExecuting(ActionExecutingContext filterContext)
。
答案 2 :(得分:0)
查看我的博客文章,了解如何在ASP.NET MVC http://bstavroulakis.com/blog/web/custom-output-caching-in-asp-net-mvc/
中创建自己的自定义输出缓存我从输出缓存中获得了以下期望
1)能够在必要时查看缓存对象及其所有子代,以便在需要时使部件无效。
2)能够在需要时禁用缓存。
3)在缓存项目之前和之后都有一些逻辑。
4)在网站上创建一些动态部分并仅加载那些部分,使网站的其余部分保持静态
5)也可以在网站的其他部分使用缓存结构。
我的行动在哪里:
public class CacheManager { #region ICacheManager Members public static void Add(string key, object value, int expireSeconds) { if (expireSeconds == CacheManagerKey.CacheLifeSpanForever) WebCache.Add(key, value, null, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration, CacheItemPriority.Normal, null); else WebCache.Add(key, value, null, DateTime.MaxValue, TimeSpan.FromSeconds(expireSeconds), CacheItemPriority.Normal, null); } public static bool Contains(string key) { return WebCache.Get(key) != null; } public static int Count() { return WebCache.Count; } public static void Insert(string key, object value) { WebCache.Insert(key, value); } public static T Get(string key) { return (T)WebCache.Get(key); } public static List GetCacheKeys() { List keys = new List(); foreach (DictionaryEntry entry in HttpContext.Current.Cache) keys.Add(entry.Key.ToString()); return keys; } public static void Remove(string key) { WebCache.Remove(key); } public static void RemoveAll() { List keys = GetCacheKeys(); foreach (string key in keys) WebCache.Remove(key); } public object this[string key] { get { return WebCache[key]; } set { WebCache[key] = value; } } #endregion public static System.Web.Caching.Cache WebCache { get { System.Web.Caching.Cache cache = null; if (HttpContext.Current != null) cache = HttpContext.Current.Cache; if (cache == null) cache = HttpRuntime.Cache; return cache; } } }
[AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = false)] public class WebCacheAttribute : ActionFilterAttribute { public int Duration { get; set; } public string CacheKey { get; set; } public Dictionary CacheParams { get; set; } public Type CacheReturnType { get; set; } public string ContentType { get; set; } public HeaderContentTypeEnum ResponseHeaderContentType{get;set;} public string CacheObj { get; set; } private readonly ICacheHoleFiller _cacheHoleFiller; public WebCacheAttribute(int duration, string cacheKey, string cacheParamsStr, HeaderContentTypeEnum response = HeaderContentTypeEnum.Html, Type type = null) { } public override void OnActionExecuting(ActionExecutingContext filterContext) { } public T GetCachedParam(Dictionary parameters, bool isAjaxRequest) { } public string GetUniqueKey(bool isAjaxRequest) { } public void OnException(ExceptionContext filterContext) { } private HtmlTextWriter tw; private StringWriter sw; private StringBuilder sb; private HttpWriter output; public override void OnResultExecuting(ResultExecutingContext filterContext) { } public override void OnResultExecuted(ResultExecutedContext filterContext) { } }
为了使某些部分动态,我使用了"Donut Caching"
最后,我创建了一个cachehelper来调用我项目中将使用webcache属性的其他方法。
var articleStr = CacheHelper.InvokeCacheMethod(typeof(HtmlHelperExtensions), "RenderArticlesCallback", new object[] { (int)articleType });
[WebCacheAttribute(CacheManagerKey.CacheLifeSpanForever, CacheManagerKey.Page_Article_Key, "articleTypeID")] public static string RenderArticlesCallback(int articleTypeID) {
public static class CacheHelper { public delegate object SourceDataDelegate(object[] args); public static T InvokeCacheMethod(Type type, string methodName, object[] args) { return (T)InvokeCacheMethod(type, methodName, null, args); } public static T InvokeCacheMethod(Type type, string methodName, object instance, object[] args) { var method = type.GetMethod(methodName); var webCache = method.ReturnParameter.Member.GetCustomAttributes(typeof(WebCacheAttribute), true).FirstOrDefault(); Dictionary cacheParameters = FixCacheParameters(method, args); T cachedObj; if (Config.CacheEnabled && webCache != null) { cachedObj = ((WebCacheAttribute)webCache).GetCachedParam(cacheParameters, false); if (cachedObj != null) return cachedObj; } T returnObj = (T)method.Invoke(instance, args); SaveCachedData(webCache, returnObj); return returnObj; } public static void SaveCachedData(object webCache, object returnObj) { if (Config.CacheEnabled && webCache != null) { var fullParamString = ((WebCacheAttribute)webCache).GetUniqueKey(false); CacheManager.Add(fullParamString, returnObj, ((WebCacheAttribute)webCache).Duration); } } public static Dictionary FixCacheParameters(MethodInfo method, object[] args) { Dictionary cacheParameters = new Dictionary(); if (args != null) { var arguments = args.ToList(); var count = 0; var argsCount = args.Length; var methodParameters = method.GetParameters().ToList(); foreach (var argument in args) { var key = methodParameters[count].Name; object value = null; if (argsCount > count) value = args[count]; if (value != null && value.GetType() == typeof(string)) value = (object)value.ToString(); if (value != null) cacheParameters.Add(key, value); count++; } } return cacheParameters; } }
有关所有这些的更多详细信息,您可以访问我的博客帖子=> Custom Output Caching in ASP.NET MVC