以编程方式控制输出缓存 - 根据参数值禁用或启用缓存

20
我们有一个标准的电子商务场景,其中包含各类别产品的分页列表。大约80%的访问者永远不会浏览超过第一页,根据类别可能还有5-10页结果,但这些结果被查看的次数要少得多。(是的,我们确实优化了第一页上显示的内容,并且具备良好的搜索功能——但那是另一种讨论)
我们无法缓存每个页面的结果,因为我们受到内存限制,但仅缓存每个类别的第一页结果的好处将是巨大的。
我知道我可以使用对象缓存来存储相关数据集来做类似的事情,但使用输出缓存是否可行,例如使用response.Cache对象?
在页面生命周期中的哪个阶段可以完成此操作?预渲染?
简化后,URL类似于“/ProductList?Category=something&Page=1”,而且我希望有类似以下的逻辑(伪代码):
If paramater "Page" equals 1
   Use output caching: vary by param = "categoryName; page"
else
   Don't use caching at all, just render the page from scratch.

我们正在使用ASP.NET 2.0,在IIS 6/win2003上。


请查看此帖子上的最后一个答案:https://dev59.com/6XNA5IYBdhLWcg3wBo9m。希望这可以帮到你。 - James
5个回答

31

您可以通过编程的方式实现与OutputCache指令相同的功能,而无需使用OutputCache指令。具体方法如下:

if (yourArbitraryCondition) {
  OutputCacheParameters outputCacheSettings = new OutputCacheParameters();
  outputCacheSettings.Duration = 60;
  InitOutputCache(outputCacheSettings);
}

在OnInit方法中这样做应该可以正常工作。显然,您可以通过设置OutputCacheParameter上的各种属性来调整缓存行为,该参数具有与指令相同的控制选项(事实上,使用指令时会生成它)。

关键点在于您只有在特定条件下执行此逻辑,而指令使其无条件执行。

更新:

作为替代方案,您可以使用上面代码中构建的低级缓存API,例如:

HttpCachePolicy cache = Response.Cache;
cache.SetCacheability(HttpCacheability.Public);
cache.SetExpires(Context.Timestamp.AddSeconds(60));
cache.VaryByParams["categoryName"] = true;

基本上,这是另一种做同样事情的方法,而不使用任何标记为“不应调用”的API。最终,两种方法都可以,所以选择你喜欢的方式。


可以工作。有什么想法为什么InitOutputCacheEditorBrowsableState.Never,并且根据http://msdn.microsoft.com/en-us/library/ms153473.aspx不应该直接调用它? - Josef Pfleger
1
嗯,这主要是因为我们最初设计时缺乏前瞻性。Page上有许多公共/受保护的API被标记为此类。它们都是由生成的代码调用的,因为使用了各种语法,我们认为用户没有任何好的理由直接调用它们。但事实上,直接调用它们并没有什么问题,事实上在某些情况下,像这里一样,它可以让您做一些使用page语法无法完成的事情。我会提交一个bug以删除那些标志,尽管对于VS2010来说已经太晚了。 - David Ebbo
还有一件事:您实际上可以直接调用低级缓存 API 来完成相同的操作(避免使用 InitOutputCache)。它可以达到相同的效果,但代码会更复杂。如果您想要这种替代方案,请告诉我。 - David Ebbo
这个能和片段缓存一起工作,用于缓存动态生成的各个控件吗?换句话说,如果一个动态控件根据一个参数变化,另一个动态控件根据另一个参数变化,会怎么样? - sagescrub
1
如果你不包括outputCacheSettings.VaryByParam = "none",顶部的代码会抛出异常。 - NSjonas

5
我很喜欢David Ebbo的回答,比我自己的好多了。
你可以使用以下方法:
<%@ OutputCache Duration="60"  VaryByParam="none" VaryByCustom="pageOne" %>

并以一种返回第一页固定键和所有其他页面随机键的方式实现。您可以(而且应该)让清理机制处理内存,但如果必须,您可以使用HttpResponse.RemoveOutputCahceItem删除缓存项。

public override string GetVaryByCustomString(HttpContext ctx, string custom)
{
    if(custom == "pageOne")
    {
        if(ctx.Request["page"] == "1")
        {
            return "1";
        }

        HttpResponse.RemoveOutputCacheItem("/Default.aspx");
        return Guid.NewGuid().ToString();
    }
    return base.GetVaryByCustomString(ctx, custom);
}

4

1
你仍然可以使用输出缓存指令,而且在我看来,与其在页面代码中堆积大量的缓存细节,不如采用基于处理 Global.asax 中的 VaryByCustom 场景的可重用解决方案。例如,如果你正在使用分页重复器方法,你可能只需要在搜索场景中排除特定页面上的任何 postback 缓存。这里 是一个代码示例,它就是这样做的。该方法仅需要使用 HttpContext 对象访问 Response.Cache.SetNoServerCaching(),在捕获任何你希望避免缓存的条件之后。希望这能帮到你。

0

我认为你应该能够使用OutputCache指令,并将VaryByParam属性设置为用于变化输出缓存的分号分隔字符串列表。

除非你只想在Page == 1时缓存


很不幸,这正是我想要的(仅当页面==1时)。如果我想缓存每一页的结果,那就很容易了,就像你所说的使用varybyparam。抱歉,我认为我没有表达清楚问题,但我想要的与每个人都习惯的正常情况略有不同,但这种差别是显著的。 - Andrew M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接