在MVC3中自定义输出缓存

时间:2012-08-23 12:11:14

标签: c# asp.net-mvc-3 outputcache

我有一些控制器操作,我希望自定义缓存。例如,假设我有一个 控制器动作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参数时,它仍然没有从缓存中删除原始请求。我在这里做错了吗?

3 个答案:

答案 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)也可以在网站的其他部分使用缓存结构。

我的行动在哪里:

  • 创建我自己的CacheManager,它将添加/删除/查找/ ...对象 在缓存中。
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