Почему ASP.NET MVC default Binder работает медленно? Это займет много времени, чтобы сделать свою работу

В текущем проекте клиент попросил возможность ответить на вопросник двумя способами: используя Wizard (по одному вопросу одновременно) и Listing (все вопросы сразу) в одной форме. Оба способа уже реализованы.

Вопросы загружаются из базы данных в главе руководства с помощью AJAX (это очень быстро). Самая большая глава на данный момент имеет 230 вопросы (каждая с 4 полями ввода HTML - ввод/текст, выбор и т.д.). Если пользователь выбирает такую ​​главу для ответа в формате Listing, <form> будет содержать примерно 920 поля, которые будут отправляться на сервер.

Я выполняю запрос AJAX POST, передавая данные с помощью метода jQuery serialize:

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

Эта сериализация занимает 207.143ms для завершения. Я получил это значение для отладки с Firebug в Firefox:

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

Снова это супер быстро...

Проблема возникает при увлажнении данных, полученных по следующему методу действий:

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

Как вы видите, опубликованные данные привязаны к IEnumerable<AnswerViewModel> questions. AnswerViewModel имеет только 4 поля для хранения каждого ответа.

Дело в том, что после нажатия кнопки "Сохранить" требуется значительное количество времени (ровно 10 секунд), чтобы попасть в точку останова по этому методу действия, то есть те 10 секунд будут потрачены в связующем устройстве модели предположительно.

Важно отметить, что я использую Steve Sanderson @Html.BeginCollectionItem helper, чтобы помочь при материализации свойств коллекции ViewModel из HTTP POST. Посмотрите, как данные попадают в ViewModel (Ключи):

enter image description here

Знаете ли вы, что я могу сделать, чтобы оптимизировать это?

Я думал об 4 обходных решениях:

  • Сохраните только измененные вопросы. Для этого мне нужно сохранить каждое значение ответа в атрибуте data при загрузке листинга и сравнить его с фактическим значением при отправке <form>, поскольку этот парень предлагает здесь.

  • Создайте объекты AnswerViewModel JavaScript на стороне клиента и передайте их методу действий. Будет ли это облегчать модель Binder?

  • Сверните мою собственную привязку к модели... но я действительно не знаю, будет ли она быстрее, чем стандартная версия ASP.NET MVC. Из того, что я прочитал, связующее устройство по умолчанию делает много отражения для установки значений/гидратации параметра модели действия, и это может быть узким местом.

  • Используйте FormCollection и перечислите через опубликованные данные, получив каждое значение по ключу и выполнив проверку вручную, как показано здесь.

Что еще вы предлагаете?


Обновление 1

Я пошел с опцией 3 и реализовал пользовательскую привязку модели: AnswerModelBinder : IModelBinder и использовал ее в этом конкретном методе действий:

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

Теперь то, что приняло 10 seconds для завершения, принимает только 2 seconds.

  • Похоже, что проверка валидации привязки модели по умолчанию [ ModelState] оказывает большое влияние на производительность.

Обновление 2

Я только что испытал это: наличие List<Guid> в качестве параметра действия и передача только 59 strings через вызов $.getJson занимали ~ 3 секунды, чтобы попасть в точку останова в первой строке метода действия. Изменение типа параметра на List<string> заставил все это работать в мгновение ока.

Интересным фактом является то, что внутри метода действия я сделал это:

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

и он мгновенно преобразует ресурсы List<string> в List<Guid>.

Конечно, есть что-то неподходящее с привязкой модели ASP.NET. Я просто хотел бы знать, что это такое...:)

Ответы

Ответ 2

Это может быть не тот ответ, который вы ищете, но он может помочь. Вместо использования FormCollection попробуйте использовать метод контроллера в модели подписи и использовать Ajax.BeginForm(). Это устранит необходимость в сериализации и позволит MVC работать. Кроме того, наличие модели со списком типа Вопрос может стоить изучить. Этот подход также, по-видимому, устранит необходимость перебора значений на почте, поскольку они уже будут в модели.

Ответ 3

Я не пробовал это, но когда я использую целочисленные индексы, связующее не имело проблем с привязкой к IEnumerable. Поскольку вы фактически не используете эти Гиды, я бы заменил их целыми числами. (0,1,2...)

Я думаю, вы могли бы сделать это легко на странице, которая отображает форму или использует JS.