2013-02-19 13 views
16

In un progetto in corso il cliente ha chiesto la possibilità di rispondere a un questionario in due modi: utilizzando uno Wizard (una domanda alla volta) e Listing (tutte le domande contemporaneamente) in un unico modulo. Entrambe le modalità sono già state implementate.Perché il modello predefinito di ASP.NET MVC è lento? Ci vuole molto tempo per fare il suo lavoro

Le domande vengono caricate dal database per capitolo del manuale utilizzando AJAX (questo è super veloce). Il più grande capitolo al momento ha domande 230 (ognuna con 4 campi di input HTML - input/testo, select, ecc.). Se l'utente seleziona tale capitolo per rispondere nel formato Listing, lo <form> conterrà a circa 920 i campi da inviare al server.

sto facendo una richiesta AJAX POST passando i dati con il metodo di jQuery serialize:

data: $("#questions :input").serialize() 

Questo serializzazione prende 207.143ms per completare. Ho ottenuto questo debug valore con Firebug in Firefox:

console.profile(); 
$("#questions :input").serialize(); 
console.profileEnd(); 

Anche in questo caso è super veloce ...

Il problema nasce quando Idratante i dati ricevuti sul seguente metodo di azione:

public async Task<ActionResult> ListSaveAsync(IEnumerable<AnswerViewModel> questions) 

Come puoi vedere, i dati pubblicati sono associati a un IEnumerable<AnswerViewModel> questions. AnswerViewModel ha solo 4 campi per memorizzare ogni risposta.

Il problema è che è necessario molto tempo (esattamente 10 secondi) dopo aver fatto clic sul pulsante Salva per ottenere un punto di interruzione su questo metodo di azione, ovvero, quei 10 secondi vengono spesi presumibilmente nel raccoglitore del modello.

Una cosa importante da ricordare è che sto usando Steve Sanderson @Html.BeginCollectionItem helper per aiutare a materializzare le proprietà della collezione ViewModel dal POST HTTP. Guarda come i dati si mette in ViewModel (Keys):

enter image description here

Sai cosa posso provare a fare per ottimizzare questo?

ho pensato a 4 soluzioni:

  1. Salvare indietro solo le domande modificate. Per fare ciò, dovrei memorizzare ogni valore di risposta in un attributo dati quando carico l'elenco e confrontarlo con il valore effettivo quando invii lo <form> come suggerisce questo here.

  2. Creare oggetti AnswerViewModel JavaScript sul lato client e passarli al metodo di azione. Questo allevierebbe il Model Binder?

  3. Roll my my binder del modello ... ma davvero non so se sarebbe più veloce di quello predefinito che viene fornito con ASP.NET MVC. Da quello che ho letto il raccoglitore modello predefinito fa un sacco di riflessi per impostare i valori/idratare il parametro del modello di azione e questo potrebbe essere il collo di bottiglia.

  4. Utilizzare FormCollection ed enumerare attraverso i dati inviati ottenendo ogni valore per chiave ed eseguendo la convalida manualmente come mostrato here.

Cos'altro suggerisci?


Aggiornamento 1

sono andato con l'opzione 3 e implementato un costume modello Binder: AnswerModelBinder : IModelBinder e utilizzati in quello specifico metodo di azione:

public async Task<ActionResult> ListSaveAsync(
      [ModelBinder(typeof(AnswerModelBinder))]List<AnswerViewModel> questions) 

Ora quello che ha preso 10 seconds per completare prende solo 2 seconds.

  • Sembra che i controlli di convalida del modello predefinito di binder [ModelState] abbiano un grande impatto sulle prestazioni.

Update 2

ho appena vissuto ancora una volta: avere un List<Guid> come parametro di azione e passando solo 59 strings attraverso una chiamata $.getJson stava prendendo ~ 3 secondi per colpire un punto di interruzione nella prima linea di il metodo di azione. Cambiando il tipo di parametro su List<string>, tutto il lavoro ha funzionato in un batter d'occhio.

Un fatto interessante è che all'interno del metodo di azione che ho fatto questo:

List<Guid> userIds = resources.Select(Guid.Parse).ToList(); 

e trasforma le risorse List<string> ad un List<Guid> istantaneamente.

Sicuramente c'è qualcosa di buggato con il raccoglitore modello ASP.NET. Vorrei solo sapere cosa è ... :)

+1

Siamo spiacenti per la risposta lenta, Leniel. Quante domande stai inviando al server? Il raccoglitore di modelli utilizza molto codice per cercare di essere molto generico sull'input di input dalla richiesta.Penso che nel tuo caso il modo migliore per risolvere sia stato il percorso che hai intrapreso. – OdeToCode

+0

@OdeToCode Grazie Scott per dare un'occhiata ... Piacevole, sei d'accordo con l'approccio personalizzato Model Binder. In questo caso specifico invierò 230 domande al server. Se faccio anche l'opzione n. 1, penso che sarà ancora più veloce ...;) Forse 1 s anziché 2 poiché JavaScript è piuttosto veloce. Devo testare questo. –

+0

@OdeToCode Semplicemente integrato il codice e implementato il workaround n. 1 che menziono sopra => quando si caricano le domande inserisco ciascun input/seleziona il valore corrente in un attributo dati e quando si salva paragono il valore corrente con quel valore precedente nei dati attributo. Quindi uso la funzione filtro di jQuery per aiutare con questo. Ora il salvataggio è istantaneo. :-) Il codice jQuery è incredibilmente veloce! –

risposta

1

Questa potrebbe non essere la risposta che stai cercando, ma potrebbe essere d'aiuto. Invece di usare FormCollection, prova ad avere un metodo controller che accetta un modello nella firma e usa Ajax.BeginForm(). Ciò eliminerà la necessità della serializzazione e consentirà a MVC di eseguire il proprio lavoro. Inoltre, potrebbe valere la pena esaminare un modello con Elenco di tipo Domanda. Questo approccio rimuoverà apparentemente anche la necessità di scorrere i valori sul post come saranno già nel modello.

+0

Grazie per l'input Anthony. Lo apprezzo molto. Dovrò provare questo ... –

0

Non l'ho provato, ma quando uso gli indici interi il raccoglitore non ha avuto problemi a legarsi a IEnumerable. Dal momento che in realtà non stai usando questi Guidi, li sostituirò con interi. (0,1,2 ...)

Immagino che si possa fare facilmente sulla pagina che esegue il rendering del modulo o utilizzando JS.

Problemi correlati