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

时间:2012-06-12 03:52:15

标签: asp.net-mvc outputcache action-filter

我已将输出缓存添加到我的应用中的几个操作,以便轻松提升性能。但是,这些操作还需要在每个请求之后递增一个计数器(它是一个视图计数器),方法是点击一个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 个答案:

答案 0 :(得分:14)

OutputCacheAttribute方式有局限性,Paul Hiles开发的名为DonutOutputCache的自定义属性有助于克服这些限制。

它支持的一个重要功能是你可以拥有一个动作过滤器,即使动作被标记为cache属性,也可以一直调用它。

对于前。您希望在5秒内缓存一个操作,同时您希望每次操作都使用LogThis过滤器接收请求时进行记录,您可以通过以下方式实现该操作,

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

来自Paul

  

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

     

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

     

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

答案 1 :(得分:1)

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

答案 2 :(得分: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(公共)),它将始终返回。

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

答案 3 :(得分:0)

使用始终执行的“验证回调”,即使应该为缓存的页面提供服务

typedef struct xyz{
    int x, y, z;
} xyz_t;

xyz_t array[] = {
    {.x = 14, .y = 16, .z = 18},
    {.x = 34, .y = 36, .z = 38},
    {.x = 64, .y = 66, .z = 68},
};

typedef struct {
    xyz_t *pointer;
} blue_t;

int main()
{
    blue_t red;
    red.pointer = &array;

    printf("%d",red.pointer[1].z);
}

注意: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(); } } 在两个地方被调用,这是设计使然(在绕过缓存时第一次调用,在提供缓存版本时秒调用)