mvc3 OutputCache RemoveOutputCacheItem RenderAction

Я провел свое исследование, но не нашел ответов.

Я использую Html.RenderAction на мастер-странице (для отображения заголовка страницы со ссылками, специфичными для разрешений пользователя). Действие украшено OutputCache, возвращает частичный контроль и кэшируется, как и ожидалось.

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

Я пытаюсь использовать метод RemoveOutputCacheItem. В качестве параметра принимает путь. Я пытаюсь установить путь к действию, используемому в Html.RenderAction. Это не делает действие недействительным.

Как я могу программно аннулировать действие?

Спасибо


person mb666    schedule 16.03.2011    source источник


Ответы (2)


Кэш для дочерних действий хранится в атрибуте OutputCacheAttribute. Свойство ChildActionCache. Проблема в том, что API, генерирующий идентификаторы дочерних действий и сохраняющий их в этом объекте, не является общедоступным (ПОЧЕМУ Microsoft??). Поэтому, если вы попытаетесь перебрать объекты в этой коллекции, вы обнаружите, что она также будет содержать кэшированное значение для вашего дочернего действия, но вы не сможете идентифицировать его, если не перепроектируете алгоритм, используемый для генерации ключей, который выглядит что-то вроде этого (как видно с Reflector):

internal string GetChildActionUniqueId(ActionExecutingContext filterContext)
{
    StringBuilder builder = new StringBuilder();
    builder.Append("_MvcChildActionCache_");
    builder.Append(filterContext.ActionDescriptor.UniqueId);
    builder.Append(DescriptorUtil.CreateUniqueId(new object[] { this.VaryByCustom }));
    if (!string.IsNullOrEmpty(this.VaryByCustom))
    {
        string varyByCustomString = filterContext.HttpContext.ApplicationInstance.GetVaryByCustomString(HttpContext.Current, this.VaryByCustom);
        builder.Append(varyByCustomString);
    }
    builder.Append(GetUniqueIdFromActionParameters(filterContext, SplitVaryByParam(this.VaryByParam)));
    using (SHA256 sha = SHA256.Create())
    {
        return Convert.ToBase64String(sha.ComputeHash(Encoding.UTF8.GetBytes(builder.ToString())));
    }
}

Таким образом, вы можете совершить следующее безумие:

public ActionResult Invalidate()
{
    OutputCacheAttribute.ChildActionCache = new MemoryCache("NewDefault");
    return View();
}

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

@Microsoft, пожалуйста, умоляю вас о ASP.NET MVC 4.0:

  1. ввести возможность кэширования пончиков в дополнение к кэшированию дырок пончиков
  2. ввести возможность легко истечь результат действия кэшированного контроллера (что-то более MVCish, чем Response.RemoveOutputCacheItem)
  3. ввести возможность легко истечь результат кэшированного дочернего действия
  4. если вы сделаете 1., то, очевидно, введите возможность истечения срока действия кэшированной части пончика.
person Darin Dimitrov    schedule 16.03.2011
comment
Интересно, как я могу просмотреть коллекцию, о которой вы упомянули? - person mb666; 16.03.2011
comment
@ user662589, используя цикл foreach: foreach (var item in OutputCacheAttribute.ChildActionCache) { ... }. - person Darin Dimitrov; 16.03.2011
comment
@Darin - Ваши молитвы были услышаны :-) Пункты 1-4 покрыты пакетом MvcDonutCaching NuGet с открытым исходным кодом. devtrends.co.uk/blog/ пончик-выход-кэширование-в-asp.net-mvc-3 - person Paul Hiles; 08.11.2011
comment
@TheFlowerGuy: опубликуйте эту ссылку в качестве ответа. Я нашел это здесь по чистой случайности! это заслуживает быть ответом! - person Korayem; 05.12.2011

Возможно, вы захотите подойти к этому по-другому. Вы можете создать собственный AuthorizeAttribute — он просто разрешит всем — и добавить переопределение метода OnCacheValidation для включения вашей логики. Если базовый OnCacheValidation возвращает HttpValidationStatus.Valid, проверьте, изменилось ли состояние, и если да, верните HttpValidationStatus.Invalid.

public class PermissionsChangeValidationAttribute : AuthorizeAttribute
{
     public override OnAuthorization( AuthorizationContext filterContext )
     {
        base.OnAuthorization( filterContext );
     }

     public override HttpValidationStatus OnCacheAuthorization( HttpContextBase httpContext )
     {
         var status = base.OnCacheAuthorization( httpContext );
         if (status == HttpValidationStatus.Valid)
         {
            ... check if the permissions have changed somehow
            if (changed)
            {
                status = HttpValidationStatus.Invalid;
            }
         }
         return status;
     }
}

Обратите внимание, что есть способы передать дополнительные данные в процессе проверки кэша, если вам нужно отслеживать предыдущее состояние, но вам придется реплицировать некоторый код в базовом классе и добавить собственный обработчик проверки кэша. Вы можете почерпнуть некоторые идеи о том, как это сделать, из моего сообщения в блоге о создании пользовательского атрибута авторизации: http://farm-fresh-code.blogspot.com/2011/03/revisiting-custom-authorization-in.html

person tvanfosson    schedule 16.03.2011
comment
Звучит как решение. Но это довольно уродливо, так как заставляет меня где-то хранить список событий, а затем запрашивать список отсюда, чтобы при необходимости аннулировать HttpValidationStatus... - person mb666; 16.03.2011
comment
@user - если возможно, я бы сохранил условия, при которых элемент был кэширован, а затем просто проверил, не изменились ли эти условия. Посмотрите на вызов SetCachePolicy в моем блоге. Параметр дополнительных данных может использоваться для хранения текущих условий. Вам понадобится CacheValidationHandler, который мог бы распаковать их и сравнить с текущими условиями при выполнении запроса кеша. - person tvanfosson; 16.03.2011
comment
@user - вы также можете посмотреть, сможете ли вы заставить свойство VaryByCustom в OutputCacheAttribute работать на вас. Возможно, есть метод, который выдает уникальное значение для каждого набора условий? - person tvanfosson; 16.03.2011
comment
Я использую VaryByCustom для возврата UserId. - person mb666; 16.03.2011
comment
@user - можете ли вы расширить его, чтобы инкапсулировать другие вещи, которые могут измениться? Скажите строку, которая инкапсулирует идентификатор пользователя и его разрешения? 134: EditUser: ViewUser: DeleteUser — тогда, если какое-либо из разрешений изменится или это другой пользователь, вы получите некэшированную страницу. - person tvanfosson; 16.03.2011
comment
Да, наверное, мне придется пойти по этому пути. Надеюсь, это будет решено в рамках MVC относительно скоро, потому что на данный момент это большая боль... - person mb666; 16.03.2011