2012-05-25 7 views
46

Как довольно опытный разработчик ASP.Net, который недавно начал использовать MVC, я немного пытаюсь изменить свое мышление с помощью традиционного «управления сервером и обработчиком событий», способ делать вещи, в более динамичный способ MVC. Я думаю, что я медленно добираюсь туда, но иногда MVC «волшебство» отбрасывает меня.ASP.Net MVC и состояние - как сохранить состояние между запросами

Мой текущий сценарий - создать веб-страницу, которая позволяет пользователю просматривать локальный файл, загружать его на сервер и повторять это, пока у него нет списка файлов для работы. Когда он доволен списком файлов (который будет отображаться в сетке на странице), он нажмет кнопку, чтобы обработать файлы и извлечь некоторые данные, которые будут храниться в базе данных.

Последняя часть не так важна, прямо сейчас я борюсь с чем-то таким же тривиальным, как создание списка файлов и сохранение этого списка между запросами. В традиционном подходе это было бы чрезвычайно просто - данные сохранялись бы в ViewState. Но в MVC мне нужно передать данные между контроллером и представлениями, и я не полностью понимаю, как это должно работать.

Думаю, я лучше опубликую мою довольно неполную попытку кодирования этого, чтобы объяснить проблему.

Для того, чтобы сохранить мой список файлов данных, я создал ViewModel, который является в основном список типизированных файлов, наряду с некоторыми дополнительными метаданными:

public class ImportDataViewModel 
{ 
    public ImportDataViewModel() 
    { 
     Files = new List<ImportDataFile>(); 
    } 

    public List<ImportDataFile> Files { get; set; } 
... 

В представлении, у меня есть форма для просмотра и загрузки файла:

<form action="AddImportFile" method="post" enctype="multipart/form-data"> 
    <label for="file"> 
    Filename:</label> 
    <input type="file" name="file" id="file" /> 
    <input type="submit" /> 
    </form> 

вид использует ViewModel в качестве своей модели:

@model MHP.ViewModels.ImportDataViewModel 

Это будет отправить файл на мое действие:

public ActionResult AddImportFile(HttpPostedFileBase file, ImportDataViewModel importData) 
    { 

     if (file.ContentLength > 0) 
     { 
      ImportDataFile idFile = new ImportDataFile { File = file }; 
      importData.Files.Add(idFile); 
     } 

     return View("DataImport", importData); 
    } 

Это действие возвращает представление для страницы DataImport вместе с ViewModel, например, содержащим список файлов.

Это хорошо работает до определенного момента, я могу просматривать файл и загружать его, и я могу видеть данные в виде viewmodel внутри действия, а затем также, если поставить точку останова внутри представления и отладить «this.Model ", Все отлично.

Но если я попытаюсь загрузить другой файл, при установке точки останова внутри действия AddImportFile параметр importData пуст. Таким образом, представление, очевидно, не передает текущий экземпляр своей модели для действия.

В образцах MVC, с которыми я прошел, экземпляр модели «магически» передается методу действия в качестве параметра, поэтому почему он пуст сейчас?

Я предполагаю, что реальная проблема заключается в моем ограниченном понимании MVC и что, вероятно, это очень простое решение. В любом случае, я был бы чрезвычайно благодарен, если бы кто-нибудь мог указать мне в правильном направлении.

+0

Вы не храните загруженные файлы в любом месте сервера. Как вы ожидаете, что они будут сохраняться между обратными передачами? Все, что у вас есть в вашем '

', - это поле ввода одного файла. Таким образом, все, что вы можете ожидать от действия контроллера, - это этот файл. Больше ничего. Нет волшебства. В ASP.NET MVC нет ViewState. –

+0

Дарин, спасибо за ваш комментарий. Верно, что я не храню файл на сервере. Я мог бы хранить его в базе данных, но это было бы пустой тратой пространства базы данных и терпения пользователя, поскольку это займет много времени, и в этом нет никакой реальной необходимости. То, что я хотел бы сделать, это просто сохранить его в памяти сервера как переменную так или иначе, пока пользователь не решит ее обработать. Затем я буду хранить вывод этой обработки в базу данных. Я думаю, что либо пытаюсь использовать MVC неправильно, либо просто не знаю, как это сделать. Я просто не знаю, какой из них еще ... – TMan

+2

Хранение файлов в серверной памяти - одна из худших вещей. Избегайте этого по любой цене. Мало того, что это потребует много памяти на вашем сервере, но не забывайте, что IIS может в любой момент переработать домен приложения, потеряв все данные, хранящиеся в памяти. И что, если вы запускаете серверную ферму?Если вы храните в памяти сервера, это означает, что только 1 сервер фермы имеет эту информацию, и если балансировщик нагрузки отправляет последующие запросы на другой сервер фермы, вы потеряете эту информацию. –

ответ

46

Прошло некоторое время с тех пор, как я опубликовал вопрос, который был довольно окрашен моим небольшим опытом и знаниями MVC. Тем не менее, я получил довольно полезный вклад, который в конечном итоге заставил меня найти решение, а также получить некоторое представление о MVC.

Что бросил меня в первую очередь, в том, что вы могли бы иметь контроллер с сильно типизированных объект в качестве параметра, например:

public ActionResult DoSomething(MyClass myObject)... 

Этот объект возник из того же контроллера:

... 
return View(myObject); 
... 

Это заставило меня поверить, что объект жил на протяжении этих двух этапов, и что я как-то мог ожидать, что вы можете отправить его на представление, сделать что-то и затем «волшебным образом» вернуть его обратно в контроллер.

После ознакомления с привязкой к модели, я понял, что это, конечно, не так. Вид полностью мертв и статичен, и, если вы не храните информацию где-то, она исчезла.

Возвращаясь к проблеме, которая выбирала и загружала файлы с клиента, и создавая список этих файлов для отображения, я понял, что в общем случае существует три способа хранения информации между запросами в MVC:

  1. Вы можете хранить информацию в виде полей в представлении, и отправить его обратно к контроллеру позже
  2. Вы можете сохраняться его в каком-то хранилище, например, файл или база данных
  3. Вы можете сохранить его в памяти сервера, используя объекты, которые проживают во всех запросах, например. Переменные сессии

В моем случае, я имел в основном два типа информации сохраняющихся: 1. Файл метаданных (имя файла, размер файла и т.д.) 2. Содержимое файла

«по «книга», вероятно, заключалась бы в том, чтобы хранить метаданные в полях формы и содержимое файла в файле или в db. Но есть и другой способ. Поскольку я знаю, что мои файлы довольно маленькие, и их будет только несколько, и это решение никогда не будет развернуто в ферме серверов или аналогичных, я хотел бы изучить параметр # 3 переменных сеанса. Файлы также не интересны для сохранения за пределами сеанса - они обрабатываются и отбрасываются, поэтому я не хотел хранить их в своем db.

После прочтения этой прекрасной статьи: Accessing ASP.NET Session Data Using Dynamics

Я был убежден. Я просто создал класс sessionbag, как описано в статье, и тогда я мог бы сделать следующее в моем контроллере:

[HttpPost] 
    public ActionResult AddImportFile(HttpPostedFileBase file) 
    { 

     ImportDataViewModel importData = SessionBag.Current.ImportData; 
     if (importData == null) importData = new ImportDataViewModel(); 

     if (file == null) 
      return RedirectToAction("DataImport"); 

     if (file.ContentLength > 0) 
     { 
      ImportDataFile idFile = new ImportDataFile { File = file }; 
      importData.Files.Add(idFile); 
     } 

     SessionBag.Current.ImportData = importData; 

     return RedirectToAction("DataImport"); 
    } 

Я прекрасно понимаю, что в большинстве случаев, это было бы плохим решением. Но для нескольких килобайт памяти сервера файлы занимают и с простотой всего этого, я думаю, что это сработало для меня очень хорошо.

Дополнительный бонус использования SessionBag заключается в том, что если пользователь ввел другой пункт меню, а затем вернулся, список файлов все равно будет там. Это не так, например. при выборе поля формы/хранения файлов.

В качестве заключительного замечания я понимаю, что SessionBag очень легко злоупотреблять, учитывая простоту использования. Но если вы используете его для того, для чего он предназначен, а именно для данных сеанса, я думаю, что это может быть мощный инструмент.

+0

Хорошее резюме и объяснение. – Lester

+1

как насчет окончания сессии? – garik

+0

@garik Решение для работы с Сеансами, которые перерабатываются, использует сервер состояния сеанса вне процесса или сервер SQL в соответствии с: https://technet.microsoft.com/en-us/library/cc754032(v=ws. 10) .aspx –

10

Что касается Uploading

1) Может быть, рассмотрим загрузчик AJAX с HTML, чтобы позволить пользователю выбрать несколько файлов перед тем они отправляются на сервер. Этот загружаемый файл BlueImp JQuery AJAX довольно увлекательный, с довольно большим api: Blueimp Jquery File Upload.Это позволит пользователям перетаскивать или многократно выбирать несколько файлов и изменять порядок файлов, включать/исключать и т. Д. Затем, когда они будут рады, они могут нажать кнопку «Загрузить», чтобы отправить на контроллер или обработчик загрузки для обработки на стороне сервера.

2) Вы можете сделать каждую загрузку сохраненной в базе данных, хотя вы бы перезагрузили всю страницу и написали дополнительную модель вида и код бритвы, чтобы добиться эффекта листинга. Это, вероятно, не будет реагировать ...

Что касается сохранения государственной WebForms/MVC

Сохранение состояния между запросами несколько черной магии и колдовства. При входе в ASP.NET MVC, поймите в нем понимание того, что веб-приложения обмениваются данными с помощью запросов и ответов. Так что продолжайте обнимать сеть как без гражданства и развивайтесь оттуда! Когда ваша модель отправляется через ваш контроллер, это ушел вместе с любыми переменными в контроллере! Однако, прежде чем он уйдет, вы можете сохранить его содержимое в базе данных для последующего поиска.

Веб-приложения не могут поддерживать истинное состояние, например, настольные приложения. Существует много способов создания ajax-фреймворков и некоторых инструментов voodoo, которые люди используют для моделирования состояния в среде HTTP. И симуляция государства - это всего лишь ложная мимика состояния. ASP.NET Web Forms пытается имитировать состояние, насколько это возможно, путем скрытия безгражданности HTTP от разработчика. Вы можете столкнуться с большой головной болью при попытке использовать свой собственный код AJAX в тандеме с кодом разметки Web Forms и собственной инфраструктурой Ajax.

Я очень рад, что вы изучаете MVC

Все шутки в сторону, если вы получите/HTTP/безгражданства менталитет MVC, это будет очень легко применять шаблоны для других супер популярных рамок такие как Ruby on Rails, SpringMVC (java), Django (python), CakePHP и т. д. Эта простая передача знаний поможет вам стать намного лучше разработчиком и получить ДЕЙСТВИТЕЛЬНО хороший Ajax.

Я очень рад, что вы изучаете MVC 3, я был с несколькими стажировками в некоторых очень крупных фирмах, у которых были эти сумасшедшие крупные проекты ASP.NET Web Forms с кодом, который пролетел везде, чтобы изменить несколько числовых значения в базе данных (-_- ') Почувствовали, как я использовал ножницы, чтобы вязать носок ребенка. Один простой неправильный ход, и все сломалось. Это было похоже на развитие в PHP, вы выходите из пота и не совсем уверены, что произошло и на какой линии. Отлаживать и обновлять было почти невозможно.

+0

хотя и немного очевидно, я бы добавил 2) a) выше, чтобы использовать скрытые поля для ведения списка файлов между запросами. В конечном итоге абстракция viewstate в веб-форме использует скрытое поле, поэтому его не такая уж плохая идея и будет проще реализовать, чем вариант базы данных. – Kiran

+0

Макс, спасибо за подробный ответ. Что касается 1) - Да, я, вероятно, попытаюсь реализовать несколько функций загрузки файлов на более позднем этапе, чтобы сделать его более удобным для пользователя, но это не основная проблема. Я думаю, что основная проблема здесь больше связана с состоянием и без гражданства. Я уже сталкивался с этой дискуссией, но я не полностью понимаю, что все это «охватывает безгражданность MVC». Вы говорите, что мы не должны внедрять состояние в наших веб-приложениях? Не все должно храниться в db. Возьмите, например, корзину покупок. Люди по своей природе являются сдержанными, поэтому почему не должны быть веб-приложениями? – TMan

+0

Киран, я думаю, ты прав. Теперь я читаю о привязке модели, которая, по-видимому, представляет значительную часть воспринимаемой «магии» в моем случае - как поля формы в представлении внезапно превращаются в строго типизированные экземпляры класса в контроллере. При попытке применить «магию» в другом сценарии, не понимая основополагающих принципов, это, конечно, пойдет не так. Думаю, я сейчас на правильном пути. – TMan

Смежные вопросы