13

我在我的应用程序中的几个操作中添加了输出缓存,以便轻松提升性能。但是,这些操作还需要在每次请求后通过点击 Redis 数据库来增加一个计数器(它是一个视图计数器)。

起初,我想我可以调整操作过滤器的执行顺序,以确保计算视图:

public class CountersAttribute : ActionFilterAttribute
{
    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        //increment my counter all clever like
        base.OnResultExecuted(filterContext);
    }
}

但这没有用;显然,OutputCacheAttribute 的行为不像正常的操作过滤器。然后我尝试实现自定义输出缓存:

public class OutputCacheWithCountersAttribute : OutputCacheAttribute
{
    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        //straight to the source to get my headcount!
        base.OnResultExecuted(filterContext);
    }
}

不,也没有用;一旦缓存了动作,动作过滤器似乎就被完全忽略了。真可惜。

那么,呃,我有什么办法(不实现自定义输出缓存提供程序)来确保我的视图被正确计数,并且干净且合理?

4

4 回答 4

14

顺便说一句,它OutputCacheAttribute有一些限制,Paul Hiles 开发的一个名为DonutOutputCache的自定义属性有助于克服这些限制。

它支持的一项重要功能是您可以拥有一个可以一直调用的操作过滤器,即使该操作是否标有缓存属性。

例如。您希望将操作缓存 5 秒,同时您希望在每次操作使用LogThis过滤器收到请求时记录下来,您可以通过以下方式简单地实现这一点,

[LogThis]
[DonutOutputCache(Duration=5, Order=100)]
public ActionResult Index()

保罗

是的,与内置的 OutputCacheAttribute 不同,即使从缓存中检索到页面,操作过滤器也会执行。唯一需要补充的是,您确实需要注意过滤器顺序。如果您的操作过滤器实现 OnResultExecuting 或 OnResultExecuted,那么这些方法将在所有情况下执行,但对于 OnActionExecuting 和 OnActionExecuted,它们只会在过滤器在 DonutOutputCacheAttribute 之前运行时执行。这是由于 MVC 在设置 filterContext.Result 属性时阻止后续过滤器执行的方式,这是我们需要为输出缓存执行的操作。

我认为您不能依赖在动作或控制器上定义动作过滤器的顺序。为了确保一个过滤器在另一个过滤器之前运行,您可以使用所有 ActionFilterAttribute 实现中存在的 Order 属性。任何未设置 order 属性的操作,默认值为 -1,这意味着它们将在具有显式 Order 值的过滤器之前执行。

因此,在您的情况下,您只需将 Order=100 添加到 DonutOutputCache 属性,所有其他过滤器将在缓存过滤器之前执行。

于 2012-06-12T04:46:25.540 回答
1

即使页面被缓存,您也可以从布局视图进行 AJAX 调用并跟踪访问者。这就是谷歌分析所做的。我建议从布局视图中执行它,因为它将在使用该布局的所有视图中执行。还有一条评论,假设您有两个布局视图:一个用于网站的公共部分,一个用于后端(仅限员工)。您可能对跟踪用户感兴趣,而不是员工,因此这是在 Layout View 进行跟踪的另一个好处。如果将来您想跟踪员工在做什么,您可以为后端布局视图添加不同的跟踪器。我希望它有所帮助。

于 2014-07-29T20:29:37.277 回答
0

原因实际上是在.NET 源中,与DonutOutputCache 无关:

public void SetCacheability(HttpCacheability cacheability)
{
  if (cacheability < HttpCacheability.NoCache || HttpCacheability.ServerAndPrivate < cacheability)
    throw new ArgumentOutOfRangeException("cacheability");
  if (HttpCachePolicy.s_cacheabilityValues[(int) cacheability] >= HttpCachePolicy.s_cacheabilityValues[(int) this._cacheability])
    return;
  this.Dirtied();
  this._cacheability = cacheability;
}

换句话说,如果你先设置NoCache(值为1),如果你尝试设置更高的值,它总是会返回,比如4(public)。

唯一的解决方案是分叉项目并将其扩展为您需要的方式,或者发送拉取请求以protected ICacheHeadersHelper CacheHeadersHelper标记DonutOutputCacheAttribute

于 2013-03-25T21:59:22.820 回答
0

Use a "Validation Callback" that is executed ALWAYS even if the cached page should be served

public class MyCacheAttribute : OutputCacheAttribute
{
    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        SaveToLog();

        httpContext.Response.Cache.AddValidationCallback(MyCallback, null);

        base.OnResultExecuting(filterContext);
    }

    // This method is called each time when cached page is going to be served
    private void MyCallback(HttpContext context, object data, ref HttpValidationStatus validationStatus)
    {
        SaveToLog();
    }
}

NOTE: the SaveToLog() is called in two places, that's by design (first call when cache is bypassed, seconds call when cached version is served)

于 2018-11-04T17:32:58.490 回答