2

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

4

3 回答 3

3

您可以使用VaryByParam属性来检查 live 选项是否为真,例如

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) 
{
    ...
}

有关清除部分,请参阅如何以编程方式清除控制器操作方法的输出缓存。

于 2012-08-23T13:44:58.143 回答
0

您无法自定义 OutputCacheAttribute 来获取行为,但您可以编写自己的 CustomCacheAttribute 来实现这一点。为此,您可以获取 OutputCacheAttribute 的来源(MVC 是开源的,因此您可以这样做),复制它并重写函数OnActionExecuting(ActionExecutingContext filterContext)

于 2012-08-23T12:54:34.477 回答
0

查看我关于在 ASP.NET MVC 中创建自己的自定义输出缓存的博客文章http://bstavroulakis.com/blog/web/custom-output-caching-in-asp-net-mvc/

我对输出缓存有以下期望,但没有得到满足

1) 能够在必要时查看缓存对象,并在需要时查看其所有子对象以使部分无效。

2)有能力在需要时禁用缓存。

3)在缓存项目之前和之后有一些逻辑。

4) 让网站上的某些部分是动态的,并且只加载那些部分,让网站的其余部分保持静态

5)在站点的其他部分也使用缓存结构。

我的行动在哪里:

  • 创建我自己的 CacheManager,它将在缓存中添加/删除/查找/...对象。
公共类 CacheManager
    {
        #region ICacheManager 成员

        公共静态无效添加(字符串键,对象值,int expireSeconds)
        {
            if (expireSeconds == CacheManagerKey.CacheLifeSpanForever)
                WebCache.Add(key, value, null, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
            别的
                WebCache.Add(key, value, null, DateTime.MaxValue, TimeSpan.FromSeconds(expireSeconds), CacheItemPriority.Normal, null);
        }

        公共静态布尔包含(字符串键)
        {
            返回 WebCache.Get(key) != null;
        }

        公共静态整数计数()
        {
            返回 WebCache.Count;
        }

        公共静态无效插入(字符串键,对象值)
        {
            WebCache.Insert(key, value);
        }

        公共静态 T 获取(字符串键)
        {
            返回 (T)WebCache.Get(key);
        }

        公共静态列表 GetCacheKeys()
        {
            列表键 = 新列表();
            foreach(HttpContext.Current.Cache 中的DictionaryEntry 条目)keys.Add(entry.Key.ToString());
            返回键;
        }

        公共静态无效删除(字符串键)
        {
            WebCache.Remove(key);
        }

        公共静态无效 RemoveAll()
        {
            列表键 = GetCacheKeys();
            foreach(键中的字符串键)
                WebCache.Remove(key);
        }

        公共对象 this[字符串键]
        {
            得到
            {
                返回 WebCache[键];
            }
            放
            {
                WebCache[键] = 值;
            }
        }

        #endregion

        公共静态 System.Web.Caching.Cache WebCache
        {
            得到
            {
                System.Web.Caching.Cache 缓存 = null;
                如果(HttpContext.Current!= null)
                    缓存 = HttpContext.Current.Cache;

                如果(缓存 == 空)
                    缓存 = HttpRuntime.Cache;

                返回缓存;
            }
        }
    }
    
  • 之后我创建了自己的属性
        [AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = false)]
        公共类 WebCacheAttribute : ActionFilterAttribute
        {
            公共 int 持续时间 { 获取;放; }
            公共字符串 CacheKey { 获取;放; }
            公共字典 CacheParams { 获取;放; }
            公共类型 CacheReturnType { 获取;放; }
            公共字符串 ContentType { 获取;放; }
            公共 HeaderContentTypeEnum ResponseHeaderContentType{get;set;}
            公共字符串 CacheObj { 获取;放; }
            私有只读 ICacheHoleFiller _cacheHoleFiller;

            公共 WebCacheAttribute(int 持续时间,字符串 cacheKey,字符串 cacheParamsStr,HeaderContentTypeEnum 响应 = HeaderContentTypeEnum.Html,类型类型 = null)
            {

            }

            公共覆盖无效 OnActionExecuting(ActionExecutingContext filterContext)
            {

            }

            public T GetCachedParam(字典参数, bool isAjaxRequest)
            {

            }

            公共字符串 GetUniqueKey(bool isAjaxRequest)
            {

            }

            公共无效 OnException(ExceptionContext filterContext)
            {

            }

            私人 HtmlTextWriter tw;
            私有 StringWriter sw;
            私人 StringBuilder 某人;
            私有 HttpWriter 输出;

            公共覆盖无效 OnResultExecuting(ResultExecutingContext filterContext)
            {

            }

            公共覆盖无效 OnResultExecuted(ResultExecutedContext filterContext)
            {

            }
    }
    
  • 为了使某些部分动态化,我使用了“甜甜圈缓存”

  • 最后,我创建了一个 cachehelper 来调用我项目中的其他方法,这些方法也将使用 webcache 属性。

var articleStr = CacheHelper.InvokeCacheMethod(typeof(HtmlHelperExtensions), "RenderArticlesCallback", new object[] { (int)articleType });
[WebCacheAttribute(CacheManagerKey.CacheLifeSpanForever, CacheManagerKey.Page_Article_Key, "articleTypeID")]
            公共静态字符串 RenderArticlesCallback(int articleTypeID)
            {
公共静态类 CacheHelper
    {
        公共委托对象 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();
            字典 cacheParameters = FixCacheParameters(method, args);
            T 缓存对象;

            if (Config.CacheEnabled && webCache != null)
            {
                cachedObj = ((WebCacheAttribute)webCache).GetCachedParam(cacheParameters, false);
                if (cachedObj != null)
                    返回缓存对象;
            }
            T returnObj = (T)method.Invoke(instance, args);
            SaveCachedData(webCache, returnObj);
            返回返回对象;
        }

        公共静态无效SaveCachedData(对象webCache,对象returnObj)
        {
            if (Config.CacheEnabled && webCache != null)
            {
                var fullParamString = ((WebCacheAttribute)webCache).GetUniqueKey(false);
                CacheManager.Add(fullParamString, returnObj, ((WebCacheAttribute)webCache).Duration);
            }
        }

        公共静态 Dictionary FixCacheParameters(MethodInfo 方法, object[] args)
        {
            字典缓存参数 = new Dictionary();
            如果(参数!= null)
            {
                var arguments = args.ToList();
                变量计数 = 0;
                var argsCount = args.Length;
                var methodParameters = method.GetParameters().ToList();

                foreach(args 中的 var 参数)
                {
                    var key = methodParameters[count].Name;
                    对象值 = 空;

                    如果(argsCount > 计数)
                        值 = 参数 [计数];

                    if (value != null && value.GetType() == typeof(string))
                        value = (object)value.ToString();

                    如果(值!= null)
                        cacheParameters.Add(key, value);

                    计数++;
                }
            }

            返回缓存参数;
        }
    }

有关所有这些的更多详细信息,您可以访问我的博客文章 => ASP.NET MVC 中的自定义输出缓存

于 2013-09-17T08:35:06.810 回答