问题 使用输出缓存和其他动作过滤器


我已将Output Caching添加到我的应用程序中的几个操作中,以便轻松提升性能。但是,这些操作还需要在每个请求(它是一个视图计数器)之后通过命中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);
    }
}

不,也没用;缓存操作后,操作过滤器似乎完全被忽略。游民。

所以,呃,有没有办法(没有实现自定义输出缓存提供程序),以确保我的观点被正确计算,干净,明智?


2830
2018-06-12 03:52


起源



答案:


OutputCacheAttribute 顺便提一下,有一个名为的自定义属性 DonutOutputCache 由Paul Hiles开发,有助于克服这些局限。

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

对于前者您希望缓存一个持续5秒的操作,同时您希望每次操作使用a收到请求时进行记录 LogThis 过滤器可以通过以下方式实现

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

保罗

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

我不认为你可以依赖于哪个动作过滤器的顺序   在动作或控制器上定义。确保一个过滤器运行   在另一个之前,您可以使用存在的Order属性   在所有ActionFilterAttribute实现上。没有的任何行动   order属性集,默认值为-1,表示它们将   在具有显式Order值的过滤器之前执行。

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


13
2018-06-12 04:46



这非常有效,除了现在我的VaryByCustom参数不起作用。在globals中调用GetVaryByCustomString(),但Donut似乎并不尊重我所做的Response.Cache更改(即,为经过身份验证的用户关闭缓存)。 - Dusda
context.Response.Cache.SetCacheability(HttpCacheability.NoCache); context.Response.Cache.SetExpires(DateTime.Now.AddMinutes(-1)); context.Response.Cache.SetNoStore(); context.Response.Cache.SetNoServerCaching();这些是我的Response.Cache更改;甜甜圈似乎忽略了它们并且无论如何都要缓存它们。 - Dusda
我将页面视图移动到ajax调用,而不是再乱用VaryByCustom。但是,给出的答案是解决这个问题的一个很好的解决方案。谢谢你:D。 - Dusda
@Dusda将页面视图移动到延迟的AJAX调用的问题是您的指标会完全扭曲。点击页面并退回(在AJAX调用完成之前)的用户将不会被计算在内。也许你一开始并不关心它们......但是你会失去对你的页面加载性能如何影响跳出率的了解(这些日子可能会非常有影响,即:用户对延迟的容忍度非常低) - Marchy


答案:


OutputCacheAttribute 顺便提一下,有一个名为的自定义属性 DonutOutputCache 由Paul Hiles开发,有助于克服这些局限。

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

对于前者您希望缓存一个持续5秒的操作,同时您希望每次操作使用a收到请求时进行记录 LogThis 过滤器可以通过以下方式实现

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

保罗

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

我不认为你可以依赖于哪个动作过滤器的顺序   在动作或控制器上定义。确保一个过滤器运行   在另一个之前,您可以使用存在的Order属性   在所有ActionFilterAttribute实现上。没有的任何行动   order属性集,默认值为-1,表示它们将   在具有显式Order值的过滤器之前执行。

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


13
2018-06-12 04:46



这非常有效,除了现在我的VaryByCustom参数不起作用。在globals中调用GetVaryByCustomString(),但Donut似乎并不尊重我所做的Response.Cache更改(即,为经过身份验证的用户关闭缓存)。 - Dusda
context.Response.Cache.SetCacheability(HttpCacheability.NoCache); context.Response.Cache.SetExpires(DateTime.Now.AddMinutes(-1)); context.Response.Cache.SetNoStore(); context.Response.Cache.SetNoServerCaching();这些是我的Response.Cache更改;甜甜圈似乎忽略了它们并且无论如何都要缓存它们。 - Dusda
我将页面视图移动到ajax调用,而不是再乱用VaryByCustom。但是,给出的答案是解决这个问题的一个很好的解决方案。谢谢你:D。 - Dusda
@Dusda将页面视图移动到延迟的AJAX调用的问题是您的指标会完全扭曲。点击页面并退回(在AJAX调用完成之前)的用户将不会被计算在内。也许你一开始并不关心它们......但是你会失去对你的页面加载性能如何影响跳出率的了解(这些日子可能会非常有影响,即:用户对延迟的容忍度非常低) - Marchy


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


1
2017-07-29 20:29





原因实际上是在.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(公共)),它将始终返回。

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


0
2018-03-25 21:59