ASP.Net MVC i state - jak zachować stan między żądaniami

Jako dość doświadczony ASP.Net programista, który dopiero co zaczął używać MVC, starałem się zmienić sposób myślenia z tradycyjnego "sterowania serwerem i obsługi zdarzeń" na bardziej dynamiczny sposób MVC. Myślę, że powoli się tam dostaję, ale czasami "Magia" MVC mnie wyrzuca.

Moim obecnym scenariuszem jest stworzenie strony internetowej, która pozwoli użytkownikowi przeglądać lokalny plik, przesłać go na serwer i powtórzyć to, dopóki nie będzie miał listy pliki do pracy. Kiedy jest zadowolony z listy plików (która będzie wyświetlana w siatce na stronie), kliknie przycisk, aby przetworzyć pliki i wyodrębnić niektóre dane, które będą przechowywane w bazie danych.

Ostatnia część nie jest tak ważna, w tej chwili zmagam się z czymś tak banalnym, jak tworzenie listy plików i utrzymywanie tej listy między żądaniami. W tradycyjnym podejściu byłoby to niezwykle proste - dane będą przechowywane w ViewState. Ale w MVC I muszę przekazać dane między kontrolerem a widokami, a ja nie do końca rozumiem, jak to ma działać.

Myślę, że lepiej opublikować moją raczej niekompletną próbę kodowania tego, aby wyjaśnić problem.

Aby zachować dane mojej listy plików, stworzyłem model widoku, który jest w zasadzie wpisaną listą plików, wraz z dodatkowymi metadanymi:

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

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

W widoku mam formularz do przeglądania i wgrywania pliku:

<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>

Widok używa viewmodel jako swojego model:

@model MHP.ViewModels.ImportDataViewModel

To wyśle plik do mojej akcji:

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);
    }

Ta akcja zwraca widok dla strony DataImport wraz z instancją viewmodel zawierającą listę plików.

Działa to ładnie do pewnego momentu, mogę przeglądać plik i przesyłać go, i mogę zobaczyć dane viewmodel wewnątrz akcji, a także jeśli umieszczę punkt przerwania w widoku i debuguję " to.Model", wszystko w porządku.

Ale wtedy, jeśli spróbuję przesłać inny plik, kiedy umieszczając punkt przerwania wewnątrz akcji AddImportFile, parametr importData jest pusty. Tak więc widok oczywiście nie przekazuje aktualnej instancji swojego modelu do akcji.

W próbkach MVC, przez które przeszedłem, instancja modelu jest" magicznie " przekazywana do metody akcji jako parametr, więc dlaczego jest teraz pusta?

Zakładam, że prawdziwym problemem jest moje ograniczone zrozumienie MVC i prawdopodobnie istnieje bardzo proste rozwiązanie tego problemu. W każdym razie, byłbym bardzo będę wdzięczny, jeśli ktoś wskaże mi właściwy kierunek.

Author: tereško, 2012-05-25

2 answers

Minęło trochę czasu, odkąd opublikowałem pytanie, które było dość zabarwione moim małym doświadczeniem i znajomością MVC. Mimo to otrzymałem kilka bardzo przydatnych informacji, które ostatecznie doprowadziły mnie do znalezienia rozwiązania, a także uzyskania wglądu w MVC.

To, co mnie zaskoczyło, to to, że możesz mieć kontroler z silnie wpisanym obiektem jako parametrem, jak to:

public ActionResult DoSomething(MyClass myObject)...

Ten obiekt pochodzi z tego samego kontrolera:

...
return View(myObject);
...
To prowadzi mnie do wiary że obiekt żył przez te dwa etapy, i że jakoś mogłem się spodziewać, że można go wysłać do widoku, coś zrobić, a następnie "magicznie"przywrócić go z powrotem do kontrolera.

Po przeczytaniu o wiązaniu modeli zrozumiałem, że tak nie jest. Widok jest całkowicie martwy i statyczny, i jeśli nie przechowasz informacji gdzieś, To Go nie ma.

Wracając do problemu, jakim było wybieranie i wgrywanie plików od klienta oraz budowanie lista tych plików do wyświetlenia, zdałem sobie sprawę, że ogólnie istnieją trzy sposoby przechowywania informacji między żądaniami w MVC:

  1. możesz przechowywać informacje w polach formularza w widoku, a następnie przesyłać je z powrotem do kontrolera
  2. możesz zapisać go w jakimś magazynie, np. w pliku lub bazie danych
  3. można go zapisać w pamięci serwera, wykorzystując obiekty, które żyją w żądaniach, np. zmienne sesji

W moim przypadku, miałem w zasadzie dwa rodzaje informacji do utrzymania: 1. Metadane pliku (nazwa pliku, Rozmiar pliku itp.) 2. Zawartość pliku

Podejście "po książce" prawdopodobnie polegałoby na przechowywaniu metadanych w polach formularza, a zawartość pliku w pliku lub w db. Ale jest też inny sposób. Ponieważ Wiem, że moje pliki są dość małe i będzie ich tylko kilka, A To rozwiązanie nigdy nie zostanie wdrożone w farmie serwerów lub podobnych, chciałem zbadać opcję # 3 zmiennych sesji. Pliki są również nie jest interesujące, aby utrzymywać się poza sesją - są przetwarzane i odrzucane, więc nie chciałem ich przechowywać w moim db.

Po przeczytaniu tego wspaniałego artykułu: dostęp ASP.NET dane sesji za pomocą Dynamics

Byłem przekonany. Po prostu utworzyłem klasę sessionbag, jak opisano w artykule, a następnie mogłem wykonać następujące czynności w moim kontrolerze:
    [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");
    }
Jestem w pełni świadomy, że w większości przypadków byłoby to złe rozwiązanie. Ale za kilka kb pamięci serwera pliki zajmują i z prostotą tego wszystkiego, myślę, że wyszło mi to bardzo ładnie.

Dodatkową zaletą korzystania z SessionBag jest to, że jeśli użytkownik wprowadzi inną pozycję menu, a następnie wróci, Lista plików nadal będzie tam. Nie byłoby tak np. przy wyborze opcji pola formularza/przechowywanie plików.

Na koniec zdaję sobie sprawę, że sesja jest bardzo łatwa do nadużycia, biorąc pod uwagę prostotę użytkowania. Ale jeśli używasz go do tego, do czego jest przeznaczony, mianowicie dane sesji, myślę, że może to być potężne narzędzie.
 46
Author: TMan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-03 18:23:59

W Sprawie Uploadu

1) może rozważyć Ajax uploader z HTML, aby umożliwić użytkownikowi wybór wielu plików zanim zostaną wysłane na serwer. Ten BlueImp jQuery AJAX File uploader jest całkiem niesamowite z całkiem świetnym api: Blueimp jQuery File Upload . Pozwoli to użytkownikom na przeciąganie i upuszczanie lub wielokrotne wybieranie kilku plików i edycję kolejności plików, dołączanie / wykluczanie itp.. Następnie, gdy będą szczęśliwi, mogą nacisnąć przycisk Prześlij, aby wysłać do kontrolera lub przesłać handler do przetwarzania po stronie serwera.

2) możesz sprawić, że każdy upload będzie kontynuowany do bazy danych, chociaż przeładowałbyś całą stronę i napisałbyś jakiś dodatkowy model widoku i Kod brzytwy, aby osiągnąć efekt listingu. To prawdopodobnie nie będzie reagować...

W zakresie przechowywania State WebForms / MVC

Utrzymywanie stanu pomiędzy żądaniami jest nieco czarną magią i voodoo. Podczas wchodzenia w ASP.NET MVC, go in it rozumiejąc, że aplikacje internetowe komunikuj się za pomocą próśb i odpowiedzi. Więc idź obejmując sieć jako bezpaństwowiec i rozwijaj się stamtąd! Kiedy twój model jest wysyłany przez kontroler, to zniknie wraz z dowolnymi zmiennymi w kontrolerze! Zanim jednak zniknie, możesz zapisać jego zawartość w bazie danych do późniejszego pobrania.

Aplikacje internetowe nie mogą zachować prawdziwego stanu, tak jak aplikacje desktopowe. Istnieje wiele sposobów frameworków ajax, a niektóre narzędzia voodoo, które ludzie używają do symulacji stanu w HTTP środowisko. A symulacja stanu jest tak naprawdę tylko fałszywą mimiką stateness. ASP.NET Web Forms stara się jak najlepiej symulować stan, ukrywając przed deweloperem bezstanowy charakter HTTP. Możesz napotkać wiele bólu głowy podczas próby wykorzystania własnego kodu AJAX w połączeniu z kodem znaczników formularzy internetowych i własnym frameworkiem Ajax.

Cieszę się, że uczysz się MVC

Wszystkie żarty na bok, jeśli dostaniesz mentalność MVC / HTTP / Bezpaństwowość, to będzie bardzo łatwo zastosować wzorce do innych super popularnych frameworków, takich jak Ruby on Rails, SpringMVC( java), Django (python), CakePHP itp... Ten łatwy transfer wiedzy pomoże Ci stać się znacznie lepszym programistą i uzyskać naprawdę dobre w Ajax.

Cieszę się, że uczysz się MVC 3, byłem z kilku staży w bardzo dużych firmach, które miały te szalone Duże ASP.NET projekty formularzy internetowych z kodem latającym wszędzie Tylko po to, aby zmienić kilka wartości liczbowych w bazie danych ( -_ -') jakbym dziergała skarpetkę dziecka nożyczkami. Jeden łatwy zły ruch i wszystko się popsuło. Czułem się jak rozwijając się w PHP, wychodzisz pocenie się i nie do końca wiem, co się stało i na jakiej linii. Debugowanie i aktualizacja było prawie niemożliwe.

 10
Author: Max Alexander,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-25 15:04:04