Кэширование ChildActions с использованием профилей кеша не будет работать? - PullRequest
17 голосов
/ 19 января 2011

Я пытаюсь использовать профили кэша для кэширования дочерних действий в моем приложении 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: Длительность должна быть положительным числом.

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

Ответы [ 5 ]

17 голосов
/ 13 декабря 2012

Я обошел проблему, создав собственный атрибут 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.

17 голосов
/ 26 января 2011

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

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

2 голосов
/ 02 февраля 2011

Вот простой способ, если:

  • Ваша основная цель - иметь возможность отключить кеш во время отладки и включить его во время развертывания
  • У вас нет сложных политик кэширования (это означает, что вы действительно должны соблюдать настройки кэширования 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() в ваше частичное представление, чтобы убедиться в соблюдении настроек кэша.

0 голосов
/ 04 июля 2013

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

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);
    }
}
0 голосов
/ 11 марта 2011

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

    /// 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 ' из моего другого ответа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...