Кэширование ChildActions с использованием профилей кеша не будет работать?

Я пытаюсь использовать профили кэша для кэширования дочерних действий в моем приложении mvc, но получаю исключение: Duration должно быть положительным числом.

Мой web.config выглядит следующим образом:

<caching>
      <outputCache enableOutputCache="true" />
      <outputCacheSettings>
        <outputCacheProfiles>
          <add name="TopCategories" duration="3600" enabled="true" varyByParam="none" />
        </outputCacheProfiles>
      </outputCacheSettings>
</caching>

И мой ребенок действует примерно так:

[ChildActionOnly]
[OutputCache(CacheProfile = "TopCategories")]
//[OutputCache(Duration = 60)]
public PartialViewResult TopCategories()
{
    //...
    return PartialView();
}

Внутри представления я просто вызываю @Html.RenderAction("TopCategories", "Category")

Но я получаю сообщение об ошибке: Сведения об исключении: System.InvalidOperationException: Длительность должна быть положительным числом.

Если я не использую профиль кеша, он работает. Подумайте, в чем проблема?

Ответ 1

Я сделал несколько копаний в вопросе и посмотрел источник mvc 3, они определенно не поддерживают никаких атрибутов, кроме Продолжительность и VaryByParam. Основная ошибка с их текущей реализацией заключается в том, что если вы не предоставите ни одного из них, вы получите исключение, говорящее вам о том, чтобы предоставить вам вместо исключения, что то, что вы пытались использовать, не поддерживается. Другая серьезная проблема заключалась в том, что они будут кэшироваться, даже если вы отключите кеширование в web.config, что кажется очень хромым и неправильным.

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

Ответ 2

У меня возникла проблема, создав собственный атрибут OutputCache, который вручную загружает Duration, VarByCustom и VarByParam из профиля:

public class ChildActionOutputCacheAttribute : OutputCacheAttribute
{
    public ChildActionOutputCacheAttribute(string cacheProfile)
    {
        var settings = (OutputCacheSettingsSection)WebConfigurationManager.GetSection("system.web/caching/outputCacheSettings");
        var profile = settings.OutputCacheProfiles[cacheProfile];
        Duration = profile.Duration;
        VaryByParam = profile.VaryByParam;
        VaryByCustom = profile.VaryByCustom;
    }
}

Преимущество этого подхода в том, что вы все равно сохраняете все свои профили только в одном месте в web.config.

Ответ 3

Здесь простой способ, если:

  • Основная цель состоит в том, чтобы отключить кеш при отладке, а включить его во время развертывания
  • У вас нет сложных политик кэширования (это означает, что вам действительно нужно уважать настройки кэширования Web.config)
  • У вас нет сложной системы развертывания, которая опирается на синтаксис кэширования Web.config
  • Идеально, если вы уже используете веб-преобразования XDT
  • Вы только предполагали, что это уже будет работать и раздражает, что это не так и требует быстрого исправления!

Все, что я сделал, был создан новый атрибут "DonutCache".

[DonutCache]
public ActionResult HomePageBody(string viewName)
{
    var model = new FG2HomeModel();

    return View(viewName, model);
}

Я сохраняю настройки кэширования в Web.config(под новым настраиваемым именем - во избежание путаницы).

<appSettings>
    <add key="DonutCachingDuration" value="5"/>   <!-- debug setting -->
</appSettings>

Я создал простой вспомогательный метод, чтобы вытащить значение.

public static class Config {
    public static int DonutCachingDuration
    {
        get
        {
            return int.Parse(ConfigurationManager.AppSettings["DonutCachingDuration"]);
        }
    }
}

К сожалению, вы можете только инициализировать [Attribute] с константой, поэтому вам нужно инициализировать атрибут в его конструкторе (к сожалению, вы не можете сказать [Attribute(Config.DonutCachingDuration)]).

Примечание. Это не мешает вам устанавливать параметр variableByParam в объявлении [DonutCache], который в настоящее время является единственным другим свойством, которое можно использовать для кэширования методов Action.

class DonutCacheAttribute : OutputCacheAttribute
{
    public DonutCacheAttribute()
    {
        // get cache duration from web.config
        Duration = Config.DonutCachingDuration;
    }
}

Просто используйте веб-преобразование XDT, и вы готовы к развертыванию с более длинным значением.

  <add key="DonutCachingDuration" value="120" 
       xdt:Locator="Match(key)" xdt:Transform="Replace"/>

Совет. Возможно, вам захочется придерживаться @DateTime.Now.ToString() в частичном представлении, чтобы убедиться, что параметр кеша соблюдается.

Ответ 4

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

    /// Use this for normal HTTP requests which need to be cached
    [OutputCache(CacheProfile = "Script")]
    public ContentResult Foo(string id)
    {
        return _Foo(id);
    }

    /// Use this for Html.Action
    public ContentResult _Foo(string id)
    {
        return View();
    }

Когда вам нужно Html.Action, вы просто вызываете _Foo вместо Foo.

@Html.Action("_Foo", "Bar").ToString();

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

Ответ 5

Это работает для меня.

public class ChildActionOutputCacheAttribute : OutputCacheAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.IsChildAction && !string.IsNullOrWhiteSpace(CacheProfile))
        {
            lock (this.GetType())
            {
                if (!string.IsNullOrWhiteSpace(CacheProfile))
                {
                    // OutputCacheAttribute for child actions only supports
                    // Duration, VaryByCustom, and VaryByParam values.
                    var outputCache = (OutputCacheSettingsSection)WebConfigurationManager.GetSection("system.web/caching/outputCacheSettings");
                    var profile = outputCache.OutputCacheProfiles[CacheProfile];
                    if (profile.Enabled)
                    {
                        Duration = profile.Duration > 0 ? profile.Duration : Duration;
                        VaryByCustom = string.IsNullOrWhiteSpace(profile.VaryByCustom)
                            ? VaryByCustom : profile.VaryByCustom;
                        VaryByParam = string.IsNullOrWhiteSpace(profile.VaryByParam)
                            ? VaryByParam : profile.VaryByParam;
                    }
                    CacheProfile = null;
                }
            }
        }
        base.OnActionExecuting(filterContext);
    }
}