2011-12-21 12 views
5

Abbiamo un'applicazione MVC3 che abbiamo creato molte piccole azioni e visualizzazioni per gestire l'immissione dei dati ovunque sia necessario. Ad esempio, se si trattava di un blog e volevamo mostrare i commenti, abbiamo un'azione e una vista di commento e possiamo posizionarla ovunque desideriamo, una vista profilo utente e una vista blog post, ecc.Impedisci chiamate DB ripetute a causa dell'approccio orientato agli oggetti nell'app MVC3

Il problema è questo a causa di ogni piccola vista o azione, è necessario effettuare una chiamata, solitamente allo stesso servizio, più volte per caricamento di una pagina a causa di tutte le altre piccole visualizzazioni che abbiamo nella nostra applicazione. Quindi su una pagina molto grande che contiene queste piccole viste, potremmo avere 80+ chiamate sql con il 40% di esse duplicate e quindi la pagina rallenta. La soluzione attuale consiste nel memorizzare nella cache alcuni dati e passare alcuni dati nel ViewBag, se possiamo, se volete, come il profilo di un utente, controllare se la sua cache o il ViewBag se non lo richiede.

Questo sembra davvero molto sporco per un modello di progettazione e l'approccio viewbag sembra orribile dal momento che deve essere passato dall'alto verso il basso. Abbiamo aggiunto alcuni dati in HttpCurrent.Items per renderlo per una richiesta (invece di memorizzare nella cache dato che i dati possono essere modificati), ma ci deve essere una soluzione pulita che non si senta in errore e sia anche pulita?

EDIT

mi è stato chiesto di essere più specifico e, mentre questa è un'applicazione aziendale interna non posso dare via a gran parte delle specifiche.

Quindi, per mettere questo in un'analogia software. Paragoniamo questo a Facebook. Immagina che questa app MVC abbia un'azione per ogni post di Facebook, quindi sotto quell'azione ha un'altra azione per il pulsante Mi piace e il numero di commenti, quindi un'altra azione per mostrare i migliori commenti all'utente. Il modo in cui è progettata la nostra app otterrebbe il profilo degli utenti corrente in ogni azione (quindi come 4 volte al minimo nella situazione precedente) e quindi l'azione figlio otterrebbe il post parent parent per verificare di avere il permesso di vederlo. Ora puoi prendere in considerazione la memorizzazione nella cache delle chiamate per ogni controllo di sicurezza, bacheca, ecc, ma credo che il caching sia per le cose che saranno necessarie nel corso della vita dell'app, non solo piccoli pezzi qua e là per correggere un errore nel modo in cui il tuo l'applicazione è progettata.

+1

Cosa si utilizza per l'accesso ai dati (ADO, NHibernate, EF)? – Phil

+0

Josh, puoi essere un po 'più specifico su come funziona la tua soluzione. Stai usando i comandi 'Html.Action()'? In che modo esattamente i tuoi comandi sono duplicati? Puoi fornire un codice o uno pseudo-codice per dimostrare il problema? In questo momento, possiamo solo indovinare e fare ipotesi. A proposito, ciò che descrivi non ha nulla a che fare con un "approccio orientato agli oggetti". È più di un approccio refactored. –

risposta

0

Vedo due potenziali punti in cui il codice potrebbe essere aiutato in base alla mia comprensione del problema.

  1. Hai troppe chiamate per pagina. In altre parole, la divisione del lavoro è troppo granulare. Potresti riuscire a combinare le chiamate al tuo servizio creando oggetti che contengono più informazioni. Se si dispone di un oggetto commenti e un oggetto con dati aggregati sui commenti, è possibile combinarli in un oggetto/una chiamata. Solo un pensiero.

  2. Caching in modo più efficace. Hai detto che stai già cercando di memorizzare nella cache i dati, ma vuoi un modo migliore per farlo. In un recente progetto su cui ho lavorato ho usato un framework AOP per fare il caching sulle chiamate WCF. Ha funzionato molto bene per lo sviluppo, ma alla fine è stato troppo lento in un sito di produzione di traffico pesante.

Il codice sarebbe venuto fuori in questo modo per una chiamata WCF (approssimativamente):

[Caching(300)] 
Comment GetComment(int commentId); 

si era appena messo un decoratore sulla chiamata WCF con un intervallo di tempo e l'AOP sarebbe preso cura del resto fino al caching. Certo, abbiamo anche utilizzato un framework di caching esterno (AppFabric) per archiviare i risultati delle chiamate WCF.

Aspect Oriented quadro (AOP): http://en.wikipedia.org/wiki/Aspect-oriented_programming Abbiamo usato l'Unità per l'AOP: Enterprise Library Unity vs Other IoC Containers

caldamente in considerazione cercando di memorizzare nella cache il servizio effettivo chiama però, in modo che li si può chiamare ai vostri cuori contenuto.

+0

Sto contrassegnando la tua domanda come accettata dal momento che abbiamo bisogno di passare a più stile di programmazione AOP per limitare/ridurre le nostre chiamate SQL. Quindi, invece di fare 5 chiamate, ne faremo uno per l'aspetto del programma con cui dobbiamo lavorare. Anche il seguente articolo ne parla un po '. http://blogs.msdn.com/b/ricom/archive/2012/01/10/performance-and-design-guidelines-for-data-access-layers.aspx – Josh

+0

Qualche tempo dopo, ero in grado di prendere questo approccio nella nostra applicazione. Abbiamo creato una nuova dashboard creata da zero. Questo dashboard estrae più elementi da più database, incluso un data warehouse, la nostra applicazione e il nostro server di scambio (per gli appuntamenti). Tempo totale per caricare IN DEV, in un secondo senza ottimizzazione. Siamo stati in grado di riutilizzare la maggior parte del nostro livello di servizio e restituire un oggetto più grande contenente tutti i dati necessari. Se volessimo utilizzare questo codice altrove, possiamo semplicemente aggiungerlo a un'altra pagina e passare l'oggetto o recuperarlo. Funziona benissimo, GRAZIE! – Josh

0

La cosa migliore da fare è creare un ActionFilter che crei e sminuisca il metodo di persistenza. Ciò garantirà che la parte più costosa dell'accesso ai dati (ovvero la creazione della connessione) sia limitata a una sola volta per richiesta.

public class SqlConnectionActionFilter : ActionFilterAttribute 
    { 
     public override void OnActionExecuting(ActionExecutingContext filterContext) 
     { 
      var sessionController = filterContext.Controller; 

      if (filterContext.IsChildAction) 
       return; 

      //Create your SqlConnection here 
     } 

     public override void OnActionExecuted(ActionExecutedContext filterContext) 
     { 
      if (filterContext.IsChildAction) 
       return; 

      //Commit transaction & Teardown SqlConnection 
     } 
    } 
+0

La connessione sql è uno degli oggetti memorizzati nella raccolta HttpCurrent.Items, quindi viene creata una sola volta per una richiesta http più il solito pool di connessioni back-end. – Josh

+0

Anche se questo è un approccio interessante, non migliora molto le prestazioni. La creazione di connessioni non crea un grosso problema di prestazioni, dal momento che le connessioni vengono comunque raggruppate per impostazione predefinita. Otterrai il massimo delle prestazioni utilizzando l'iniezione delle dipendenze con un oggetto con scopo scopime. –

2

si è in grado di sostituire uno dei tuoi @ Html.Action() chiama con @ Html.Partial() chiama, passando i dati del modello, invece di basarsi su un metodo di azione per ottenere dal db?

È possibile creare un CompositeViewModel che contenga gli altri ViewModels come proprietà. Ad esempio, potrebbe contenere una proprietà UserViewModel, una proprietà BlogPostViewModel e una proprietà Collection<BlogComment>.

Nel metodo di azione che restituisce la vista contenitore/master, è possibile ottimizzare l'accesso ai dati. Sembra che tu abbia già un sacco di codice ripetibile estratto da un servizio, ma non hai inserito alcun codice, quindi non sono sicuro di quanto possa essere ASCIUTTO questo approccio.

Ma se si può fare questo senza ripetere un sacco di codice da vostre azioni bambino, è possibile utilizzare @Html.Partial("~/Path/to/view.cshtml", Model.UserViewModel) nella vista maestro, e mantenere il metodo di azione bambino per altre pagine che non hanno una così pesante caricare.

+0

Sono andato a provare questo approccio, ma la quantità di refactoring che avremmo dovuto fare sarebbe stata immensa e alcuni dei nostri controllori sarebbero sfuggiti di mano. Dato che utilizziamo l'iniezione di dipendenza, quei controllori avrebbero passato 10-20 server nel costruttore, il che è inaccettabile. Ciò sconfigge la capacità di mantenere il controller facendo una sola e unica cosa in quanto avrebbe dovuto fare molti. Stiamo andando ad AOP e implementiamo il caching per ridurre ed eliminare le chiamate extra. – Josh

0

Il problema è: se stai facendo la query 80 volte, allora stai colpendo il db 80 volte. Mettere una cache con scope richiesta è la soluzione migliore. Il modo più elegante di implementarlo è tramite AOP, quindi il tuo codice non si preoccupa di quel problema.

Problemi correlati