2011-01-18 5 views
17

Sto cercando di utilizzare i profili di cache per la memorizzazione nella cache di azioni figlio nella mia applicazione mvc, ma ottengo un'eccezione: la durata deve essere un numero positivo.Caching Child Le azioni che utilizzano i profili di cache non funzioneranno?

mio web.config si presenta così:

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

E la mia azione bambino qualcosa di simile:

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

All'interno di una vista che basta chiamare @Html.RenderAction("TopCategories", "Category")

ma ottengo un errore : Dettagli eccezione: System.InvalidOperationException: la durata deve essere un numero positivo.

Se non utilizzo il profilo cache funziona. Hai un'idea di qual è il problema?

risposta

17

ho fatto un po 'scavare su un related question e guardando MVC 3 fonte, che sicuramente non supportano qualsiasi attributo diverso Durata e VaryByParam. Il bug principale della loro attuale implementazione è che se non fornisci uno di questi, riceverai un'eccezione che ti dice di fornirli, invece di un'eccezione dire che ciò che hai provato ad usare non è supportato. L'altro grosso problema era che memorizzeranno la cache anche se si disabilita la memorizzazione nella cache in web.config, il che sembra davvero scadente e non corretto.

Il problema più grande che ho avuto con tutto ciò è che non stanno usando lo stesso attributo che funziona in entrambi i punti di vista e una vista parziale, ma in realtà dovrebbe probabilmente essere di 2 diversi attributi poiché la vista parziale è così limitata e si comporta molto in modo diverso, almeno nella sua attuale implementazione.

+0

Grazie per la risposta :) – frennky

+2

Ecco un bell'articolo che spiega il problema: http://www.dotnetcurry.com/ShowArticle.aspx?ID=665 – frennky

+0

Ho fissato questo e presentato una richiesta di pull oggi: http://aspnetwebstack.codeplex.com/SourceControl/network/forks/ssmith/OutputCacheAttributeBugfix/contribution/4100 così come scrivere un articolo su come risolvere da solo (tramite richiesta pull) oltre a commentare su SO: http://ardalis.com/how-to-contribute-to-aspnet-yourself – ssmith

2

Ecco un modo semplice se:

  • Il tuo obiettivo fondamentale è quello di essere in grado di disabilitare la cache durante il debug, e abilitarlo durante la distribuzione
  • politiche di caching Se associ complicate (ciò significa che devi veramente rispettare le impostazioni di cache di Web.config)
  • Non hai un complicato sistema di distribuzione che si basa sulla sintassi di caching di Web.config
  • Ideale se stai usando XDT web transformations già
  • Hai appena pensato che avrebbe già funzionato e sono infastidito dal fatto che non funzionasse e necessitasse di una soluzione rapida!

Tutto ciò che ho fatto è stato creato un nuovo attributo 'DonutCache'.

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

    return View(viewName, model); 
} 

devo conservare la mia impostazione di memorizzazione nella cache in web.config (con un nuovo nome personalizzato - in modo da evitare confusione).

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

ho creato un metodo di supporto semplice per tirare fuori il valore.

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

Purtroppo è possibile inizializzare solo [Attribute] con una costante, quindi è necessario inizializzare l'attributo nel suo costruttore (non puoi solo dire [Attribute(Config.DonutCachingDuration)] purtroppo).

Nota: Ciò non impedisce impostazione che 'VaryByParam' nella dichiarazione [DonutCache] - che è attualmente l'unica altra proprietà che è utilizzabile per il caching dei metodi di azione.

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

Basta utilizzare un web di trasformazione XDT e si è pronti per la distribuzione con un valore più lungo.

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

Suggerimento: Probabilmente vuole attaccare un @DateTime.Now.ToString() nella vostra visione parziale per assicurarsi che l'impostazione della cache viene rispettato.

-1

In alcuni casi può essere appropriato creare un secondo metodo di azione, con la cache disabilitata chiamata dall'azione principale.

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

Quando avete bisogno Html.Action basta chiamare _foo invece di Foo.

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

È quindi possibile fare affidamento sulla pagina padre per eseguire la memorizzazione nella cache. Se questo non è appropriato (perché non si desidera memorizzare nella cache l'intera pagina), è possibile utilizzare lo 'DonutCacheAttribute' dall'altra mia risposta.

+0

Ciò non consente l'uso di [ChildActionOnly], quindi non è una risposta – YEH

17

ho preso in giro il problema creando un attributo personalizzato OutputCache, che carica manualmente il Duration, VarByCustom e VarByParam dal profilo:

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

Il vantaggio di questo approccio è che si arriva a mantenere ancora tutto il vostro profili in un solo punto nel web.config.

+0

Quando ho postato la domanda, ho pensato che stavo facendo qualcosa di sbagliato e non sospettavo che fosse un problema di struttura. Alla fine sono andato con una soluzione simile. – frennky

+0

Sì, mi è successa la stessa cosa. E poiché sono stato in grado di elaborare una soluzione relativamente semplice, ho deciso di aggiungere id a questo vecchio thread nel caso in cui aiuti qualcun altro. –

+3

Funziona alla grande - ho dovuto comunque eseguire l'override di 'OnActionExecuting' e chiamare' base.OnActionExecuting' solo quando il profilo della cache è stato abilitato nel web.config. Altrimenti il ​​temuto "Durata" - errore comparso durante l'impostazione di 'enabled =" false "'. – marapet

-1

Questo funziona per me.

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