2013-02-19 13 views
16

W bieżącym projekcie klient poprosił o możliwość udzielenia odpowiedzi na kwestionariusz na dwa sposoby: za pomocą pytania: Wizard (jedno pytanie za jednym razem) i Listing (wszystkie pytania naraz) w jednym Formularz. Oba sposoby są już wdrożone.Dlaczego domyślny spinacz modelu ASP.NET MVC jest wolny? Wykonanie tego zajmuje dużo czasu.

Pytania są ładowane z bazy danych w rozdziale podręcznika przy użyciu technologii AJAX (jest to bardzo szybkie). Największy rozdział w tej chwili ma pytania 230 (każdy z 4 polami wprowadzania HTML - input/text, select, etc). Jeśli użytkownik wybierze taki rozdział do odpowiedzi w formacie Listing, <form> będzie zawierał około 920 pól do wysłania na serwer.

robię żądanie AJAX POST przekazując dane z serialize metody jQuery:

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

Ten serializacji zaczyna 207.143ms aby zakończyć. Dostałem tę wartość debugowania Firebug Firefox:

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

znowu jest super szybki ...

Problem pojawia się, gdy Hydrating dane otrzymane w następujący sposób działanie:

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

Jak widzisz, opublikowane dane są danymi związanymi z IEnumerable<AnswerViewModel> questions. AnswerViewModel ma tylko 4 pola do przechowywania każdej odpowiedzi.

Chodzi o to, że zajmuje dużo czasu (dokładnie 10 sekund) po kliknięciu przycisku Zapisz, aby trafić punkt przerwania w tej metodzie akcji, czyli te 10 sekund są prawdopodobnie wydawane w segregatorze modelu.

Należy wspomnieć, że używam narzędzia Steve Sandersona @Html.BeginCollectionItem helper, aby pomóc w zmaterializowaniu właściwości kolekcji ViewModel z testu HTTP POST. Zobacz, w jaki sposób dane dostaje w ViewModel (klawisze):

enter image description here

Czy wiesz, co mogę spróbować zrobić, aby zoptymalizować to?

myślałem o 4 obejścia:

  1. Zapisz powrotem tylko zmodyfikowane pytania. Aby to zrobić, muszę zapisać każdą wartość odpowiedzi w atrybucie danych podczas ładowania listy i porównać ją z rzeczywistą wartością podczas przesyłania <form>, ponieważ ten facet sugeruje here.

  2. Utwórz AnswerViewModel obiektów JavaScript po stronie klienta i przekaż je do metody działania. Czy to złagodzi model Binder?

  3. Rzuć mój własny segregator ... ale naprawdę nie wiem, czy byłby szybszy niż domyślny, który jest dostarczany z ASP.NET MVC. Z tego, co przeczytałem, domyślny model spoiwa robi wiele refleksji, aby ustawić wartości/nawodnić parametr modelu działania, a to może być wąskie gardło.

  4. Użyj FormCollection i wylicz na podstawie opublikowanych danych, uzyskując każdą wartość kluczem i wykonując sprawdzanie ręcznie, jak pokazano here.

Co jeszcze sugerujesz?


Update 1

poszedłem z opcji 3 i wdrożony model niestandardowy Binder: AnswerModelBinder : IModelBinder i stosować go w tej konkretnej metody działania:

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

Teraz co miało 10 seconds zakończenia zajmuje tylko 2 seconds.

  • Wygląda jak domyślne sprawdzanie poprawności bindownicy modelu [ModelState] ma duży wpływ na wydajność.

Aktualizacja 2

właśnie doświadczył go jeszcze raz: o List<Guid> jako parametr działania i przechodząc tylko 59 strings przez $.getJson rozmowy brał ~ 3 sekundy uderzy punkt przerwania w 1. linii metoda działania. Zmiana typu parametru na List<string> sprawiła, że ​​cała sprawa działała w mgnieniu oka.

Ciekawostką jest fakt, że wewnątrz metody działania zrobiłem to:

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

i przekształca zasoby List<string> do List<Guid> natychmiast.

Na pewno jest coś buggy z segregatora ASP.NET modelu. Chciałbym tylko wiedzieć, co to jest ... :)

+1

żal powolnej odpowiedzi, Leniel. Ile pytań wysyłasz na serwer? Segregator modelu używa dużej ilości kodu, aby uzyskać bardzo ogólne informacje o analizie danych wejściowych z żądania.Myślę, że w twoim przypadku najlepszym sposobem na rozwiązanie była ścieżka, którą podjąłeś. – OdeToCode

+0

@OdeToCode Dzięki Scott za spojrzenie ... Miło, że zgadzasz się z niestandardowym podejściem Binder Model. W tym konkretnym przypadku wysyłam 230 pytań na serwer. Jeśli zrobię też opcję nr 1, myślę, że będzie jeszcze szybciej ...;) Może 1s zamiast 2s, ponieważ JavaScript jest dość szybki. Musisz to przetestować. –

+0

@OdeToCode Po prostu uzupełniłem kod i zaimplementowałem obejście # 1 Wspominam powyżej => przy ładowaniu pytań Wkładam każde wejście/wybieram bieżącą wartość w atrybucie danych i podczas zapisu porównuję bieżącą wartość z tą poprzednią wartością w danych atrybut. Następnie używam funkcji filtrowania jQuery, aby pomóc w tym. Teraz Oszczędzanie jest natychmiastowe. :-) Kod jQuery jest imponująco szybki! –

Odpowiedz

1

To nie może być odpowiedź, której szukasz, ale może pomóc. Zamiast korzystać z FormCollection, spróbuj mieć metodę kontrolera akceptującą model w sygnaturze i użyj Ajax.BeginForm(). To usunie potrzebę serializacji i pozwoli MVC to zrobić. Warto również przyjrzeć się modelowi z Listą typu Pytanie. To podejście pozornie eliminuje również potrzebę iteracji wartości na stanowisku, ponieważ będą one już w modelu.

+0

Dzięki za wejście Anthony. Bardzo to doceniam. Będę musiał spróbować tego ... –

0

Nie próbowałem tego, ale kiedy używam indeksów całkowitoliczbowych, spoiwo nie miało problemów z wiązaniem z IEnumerable. Ponieważ nie używasz tych Guidów, zastąpiłbym je liczbami całkowitymi. (0,1,2 ...)

Sądzę, że można to łatwo zrobić na stronie renderującej formularz lub przy użyciu JS.

Powiązane problemy