2012-06-12 17 views
11

Ho aggiunto la cache di output a un paio di azioni nella mia app per alcuni potenziamenti di prestazioni semplici. Tuttavia, queste azioni devono anche incrementare un contatore dopo ogni richiesta (è un contatore di visualizzazioni) colpendo un db Redis.Utilizzo della cache di output e altri filtri di azione

In un primo momento, ho pensato che avrei potuto solo regolare l'ordine in cui i filtri di azione eseguire per garantire la visualizzazione viene conteggiato:

public class CountersAttribute : ActionFilterAttribute 
{ 
    public override void OnResultExecuted(ResultExecutedContext filterContext) 
    { 
     //increment my counter all clever like 
     base.OnResultExecuted(filterContext); 
    } 
} 

Ma che non ha funzionato; apparentemente OutputCacheAttribute non si comporta come un normale filtro azione. Quindi ho provato a implementare una cache di output personalizzata:

public class OutputCacheWithCountersAttribute : OutputCacheAttribute 
{ 
    public override void OnResultExecuted(ResultExecutedContext filterContext) 
    { 
     //straight to the source to get my headcount! 
     base.OnResultExecuted(filterContext); 
    } 
} 

No, non ha funzionato neanche; i filtri azione sembrano essere completamente ignorati una volta che un'azione viene memorizzata nella cache. Bummer.

Quindi, c'è un modo (senza implementare un fornitore di memorizzazione nella cache di output personalizzato) per assicurarmi che le mie visualizzazioni siano correttamente calcolate e pulite?

risposta

13

Il OutputCacheAttribute ha limitazioni a proposito e c'è un attributo personalizzato denominato DonutOutputCache sviluppato da Paul Hiles aiuta a superare i limiti.

Una delle funzionalità importanti supportate è che si può avere un filtro di azioni che può essere chiamato tutte le volte anche se l'azione è contrassegnata con attributo di cache o meno.

Per es. si desidera memorizzare nella cache l'azione per la durata di 5 secondi e, allo stesso tempo che si desidera registrare ogni volta che l'azione riceve una richiesta utilizzando un filtro LogThis è possibile ottenere che semplicemente da sotto,

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

Da Paul,

Sì, a differenza del built-in OutputCacheAttribute, i filtri di azione eseguiranno anche quando una pagina viene recuperata dalla cache. L'unica avvertenza da aggiungere è che devi fare attenzione sull'ordine del filtro. Se il tuo filtro azione implementa OnResultExecuting o OnResultExecuted allora questi metodi verranno eseguiti in tutti i casi, ma per OnActionExecuting e OnActionExecuted, verranno eseguiti solo se il filtro viene eseguito prima di DonutOutputCacheAttribute. Ciò è dovuto a il modo in cui MVC impedisce l'esecuzione dei filtri successivi quando si imposta la proprietà filterContext.Result, che è ciò che dobbiamo fare per la cache di output .

Non credo che si possa fare affidamento sull'ordine in cui i filtri di azione sono definiti su un'azione o un controller. Per garantire che un filtro venga eseguito prima di un altro , è possibile utilizzare la proprietà Ordine che è presente su tutte le implementazioni ActionFilterAttribute. Qualsiasi azione senza il set di proprietà dell'ordine , ha un valore predefinito di -1, il che significa che eseguiranno prima dei filtri che hanno un valore Ordine esplicito.

Pertanto, nel tuo caso, puoi semplicemente aggiungere Ordine = 100 all'attributo DonutOutputCache e tutti gli altri filtri verranno eseguiti prima del filtro di memorizzazione nella cache prima di .

+0

Questo funziona meravigliosamente, tranne che ora il mio param VaryByCustom non funziona. GetVaryByCustomString() viene chiamato in globals, ma Donut non sembra onorare le modifiche apportate a Response.Cache (ovvero, disattivare la cache per gli utenti autenticati). – Dusda

+0

context.Response.Cache.SetCacheability (HttpCacheability.NoCache); context.Response.Cache.SetExpires (DateTime.Now.AddMinutes (-1)); context.Response.Cache.SetNoStore(); context.Response.Cache.SetNoServerCaching(); Queste sono le mie modifiche a Response.Cache; La ciambella sembra ignorarli e conservarli tutti in ogni caso. – Dusda

+0

Ho spostato le visualizzazioni di pagina su una chiamata ajax, invece di confondermi con VaryByCustom. Tuttavia, la risposta fornita è un'ottima soluzione a questo problema. Grazie: D. – Dusda

0

La ragione è in realtà la fonte .NET, e nulla a che fare con la 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; 
} 

In altre parole, se si imposta NoCache prima (un valore pari a 1), sarà sempre restituire se si tenta di impostare un valore superiore, ad esempio 4 (pubblico).

L'unica soluzione è quella di un fork del progetto ed estenderlo a come avete bisogno, o forse inviare una richiesta di pull per contrassegnare protected ICacheHeadersHelper CacheHeadersHelper in DonutOutputCacheAttribute

1

È possibile effettuare una chiamata AJAX dal layout di visualizzazione e monitorare i visitatori anche se la pagina è memorizzata nella cache. Questo è ciò che fa Google Analytics. Vi consiglio di farlo dalla Vista Layout perché verrà eseguito in tutte le viste che utilizzano quel layout. Un altro commento, supponiamo che tu abbia due viste Layout: una per la parte pubblica del sito e una per il back-end (solo dipendenti). Probabilmente sarai interessato a rintracciare gli utenti, non i dipendenti, quindi questo è un altro vantaggio del tracciamento nella vista Layout. Se in futuro si desidera tenere traccia di ciò che i dipendenti stanno facendo, è possibile aggiungere un tracker diverso per la vista Layout back-end. Spero che aiuti.

Problemi correlati