ASP.Net MVC и состояние - как сохранить состояние между запросами - программирование
Подтвердить что ты не робот

ASP.Net MVC и состояние - как сохранить состояние между запросами

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

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

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

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

Чтобы сохранить данные списка файлов, я создал модель представления, которая представляет собой типизированный список файлов, а также некоторые дополнительные метаданные:

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 и что, вероятно, это очень простое решение. В любом случае, я был бы чрезвычайно благодарен, если бы кто-нибудь мог указать мне в правильном направлении.

4b9b3361

Ответ 1

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

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

public ActionResult DoSomething(MyClass myObject)...

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

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

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

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

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

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

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

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

После прочтения этой замечательной статьи: Доступ к данным сеанса ASP.NET с использованием динамики

Я был убежден. Я просто создал класс 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 очень легко злоупотреблять, учитывая простоту использования. Но если вы используете его для того, для чего он предназначен, а именно для данных сеанса, я думаю, что это может быть мощный инструмент.

Ответ 2

Что касается загрузки

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

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

Что касается сохранения состояния WebForms/MVC

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

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

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

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

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