Работа с выходным кэшем и другими фильтрами действий

Я добавил выходное кэширование к нескольким действиям в моем приложении для небольшого повышения производительности. Тем не менее, эти действия также должны увеличивать счетчик после каждого запроса (это счетчик просмотров), нажимая Redis db.

Сначала я решил, что могу просто отрегулировать порядок, в котором выполняются фильтры действий, чтобы обеспечить просмотр:

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);
    }
}

Нет, тоже не работал; фильтры действий кажутся полностью проигнорированными после того, как действие будет кэшировано. Облом.

Итак, есть ли какой-либо способ (без внедрения пользовательского кэширующего поставщика вывода) для обеспечения того, чтобы мои представления учитывались правильно, что было чисто и разумно?

Ответ 1

OutputCacheAttribute имеет ограничения, и существует специальный атрибут DonutOutputCache, разработанный Paul Hiles, помогает преодолеть ограничения.

Одна из важных функций, которые она поддерживает, - это иметь фильтр действий, который можно вызвать все время, даже если действие отмечено с атрибутом кеша или нет.

Например, вы хотите кэшировать действие в течение 5 секунд и в то же время вы хотите регистрировать каждый раз, когда действие получает запрос с использованием фильтра LogThis, который вы можете достичь, просто ниже,

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

Из Paul,

Да, в отличие от встроенного атрибута OutputCacheAttribute фильтры действий будут выполняются даже при извлечении страницы из кеша. Единственное предостережение добавить, что вам нужно быть осторожным относительно порядка фильтрации. Если ваш фильтр действий реализует OnResultExecuting или OnResultExecuted то эти методы будут выполняться во всех случаях, но для OnActionExecuting и OnActionExecuted, они будут выполняться только в том случае, если фильтр запускается до атрибута DonutOutputCacheAttribute. Это связано с способ, которым MVC предотвращает выполнение последующих фильтров, когда вы установите свойство filterContext.Result, что мы должны сделать для выходное кэширование.

Я не думаю, что вы можете положиться на порядок, в котором действуют фильтры действий определяются действием или контроллером. Чтобы обеспечить выполнение одного фильтра перед другим вы можете использовать имеющееся свойство Order на всех реализациях ActionFilterAttribute. Любые действия без set, по умолчанию значение -1, что означает, что они будут выполнить перед фильтрами, которые имеют явное значение Order.

Поэтому в вашем случае вы можете просто добавить Order = 100 в Атрибут DonutOutputCache и все остальные фильтры будут выполняться до фильтр кеширования.

Ответ 2

Вы можете сделать вызов AJAX из представления макета и отслеживать своих посетителей, даже если страница кэширована. Это то, что делает Google Analytics. Я рекомендую сделать это из представления макета, потому что он будет выполнен во всем представлении, которое использует этот макет. Еще один комментарий, скажем, что у вас есть два вида макета: один для публичной части сайта и один для фоновых (только для сотрудников). Вероятно, вам будет интересно отслеживать пользователей, а не сотрудников, поэтому это еще одно преимущество отслеживания в макетном представлении. Если в будущем вы хотите отслеживать, что делают сотрудники, вы можете добавить другой трекер для внешнего вида макета. Надеюсь, это поможет.

Ответ 3

Причина на самом деле в источнике .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