ASP NET MVC - przetrzymywanie danych między żądaniami

0

Cześć, podczas nauki w/w frameworka natrafiłem na pewne ograniczenie (może coś źle robię). Powiedzmy, że mamy tradycyjnie kontroler Home oraz w nim 2 metody akcji przyjmijmy : Metoda1 oraz Metoda2. Powiedzmy, że w Metoda1 pobieram jakieś elementy (przyjmijmy z formularza - np logowania : login haslo). I teraz czy jest możliwość by przechować gdzieś te dane (chcę przechować dane między żądaniami). I przy wywołaniu Metoda2 pobrać te dane? Wiem, że : prawdopodobnie można to zapisać w sesji(?), lub w bazie danych - ale mi to rozwiązanie nie odpowiada. Myślałem, by spróbować to rozwiązać w taki sposób:

    public class HomeController : Controller
    {
        private string login, password;
        public ActionResult Metoda1()
        {
          //Pobieram dane z formularza i przypisuje do zmiennych
          login=pobieram z formularza;
            password=pobieram z formularza;
            return View();
        }

        public ActionResult Metoda2()
        {
            //Tutaj chce odczytac wartosci ze zmiennych login i password - ale zmienne sa rowne null
            return View();
        }
    }

Ale tak jak zapisalem w komentarza, mimo wywolanie najpierw metoda1 i przypisaniu wartosciom zmiennym, to przy wywolaniu Metoda2 wartosci zmiennych mają przypisaną wartość null

3

Długość życia twoich zmiennych

private string login, password;

Jest tak długa, jak długość życia instancji tego Controllera.

A prościej mówiąc, gdy przychodzi żądanie HTTP na np. localhost:5000/index, to do obsługi tego żądania jest (afaik) wstrzykiwana instancja (w twoim przypadku) HomeController i jakaś akcja obsługująca to żądanie np. Metoda1. Gdy zostanie wykonana metoda Metoda1 i kończy się żądanie, to ta instancja HomeController "umiera", a przy następnym żądaniu do Metoda2 powstaje nowa instancja HomeController, więc nie ma ona tych danych.

a przynajmniej tak mi się wydaje :D

Jeżeli uczynisz te logic i password statycznymi (static), to powinny nie zniknąć.

private static string login;

chociaż nie jest to dobre rozwiązanie do czegokolwiek więcej niż testy, bo gdy np. dwie osoby zaczną korzystać z serwisu, to dane będą się mieszać/nadpisywać itd.

1

No ale w przypadku static wypadało by użyć mechanizmów blokowania dostępu wielowątkowego.

1

Używając static aplikacja staje się używalna dla jednego użytkownika, więc po co w ogóle jakiś login i hasło?

@amator963 - jeśli chodzi Ci faktycznie o uwierzytelnianie użytkowników w aplikacji, to po prostu użyj wbudowanych mechanizmów, które są zawarte nawet w szablonach projektów.
Jeśli chodzi o coś innego, to napisz o co, wtedy będzie można pomyśleć nad dopasowanym rozwiązaniem. Pola kontrolerów to nie jest sensowne miejsce na przechowywanie danych.

0

Okej, już rozjaśniam o co mi chodziło z tymi zmiennymi :). Podam na swoim praktycznym przykładzie oraz na tym co chcę osiągnąć.
Mam 2 Modele:

 public class User
    {
        public string Login { get; set; }
        public string Password { get; set; }
    }

oraz :

 public class UserViewModel
    {
        public List<User> Users { get; set; }
        public User User { get; set; }
        public int ColumnCount;
        public UserViewModel()
        {
            Users = new List<User>();
            ColumnCount = 1;
        }
    }

HomeController:

public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
        [HttpGet]
        public ActionResult About()
        {
            UserViewModel model = new UserViewModel();

            return View(model);
        }
        [HttpPost]
        public ActionResult About(UserViewModel model)
        {
            ++model.ColumnCount;
            return View(model);
        }
    }

Widok :

@model WebApplication5.Models.UserViewModel

@{
    ViewBag.Title = "About";
}
<h2>@ViewBag.Title.</h2>
<h3>@ViewBag.Message</h3>

<p>Use this area to provide additional information.</p>

@using (Html.BeginForm("About", "Home", FormMethod.Post))
{
<input type="submit" value="Dodaj wiersz">
<table>
    <thead>
        <tr>
            <td>Login</td>
            <td>Hasło</td>
        </tr>
    </thead>
    @for (int i = 0; i < Model.ColumnCount; i++)
    {
    <tr>
        <td>
            <input type="text" name="Login" value="Mickey">
            @*@Html.TextBoxFor(x=>x.User.Login)*@
        </td>
        <td>
            <input type="text" name="Password" value="Mouse">
            @*@Html.TextBoxFor(x => x.User.Password)*@
        </td>
    </tr>
    }
</table>

}

Oraz mój cel:

  1. W widoku mam przycisk Dodaj wiersz. Domyślnie jest wyświetlany jeden wiersz z dwoma polami do wpisania. Ale po wykonaniu akcji Dodaj wiersz chcę by dodać kolejny wiersz w formularzu (w metodzie About która jest POST zwiększam licznik ColumnSize o 1 - wydaje się logiczne i wtedy przekazuję ten model z powrotem do widoku. W widoku znajduję się pętla for która wykorzystuje wartość ColumnSize. Tylko problem jest taki, że takie rozwiązanie zadziała tylko raz. Po wykonaniu pierwszy raz akcji Dodaj kolumnę faktycznie pojawią się 2 wiersze, ale gdy wykonam kolejny raz akcję model tak jakby się resetował i przy wejściu do metody POST nie zawierał ColumnSize=2 tylko dalej wartość defaultową równą =1.
  2. W widoku zakomentowałem pewny kod. Mój cel : chcę w zależności od utworzonej liczby wierszy Login / Hasło utworzyć odpowiednią liczbę modeli i dodać ją do listy w ViewModelu (ehh, ciężki mi to opisać). Inaczej : każdy utworzony wiersz w widoku to jeden model. Powiedzmy, że po wykonnaiu 3 x akcji Dodaj kolumnę i uzupełnieniu pól (mam 3 modele) i chcę je dodać do listy by powiedzmy w metodzie About POST mieć listę 3 użytkowników (z uzupełnionymi danymi )
2
amator963 napisał(a):

Ale po wykonaniu akcji Dodaj wiersz chcę by dodać kolejny wiersz w formularzu (w metodzie About która jest POST zwiększam licznik ColumnSize o 1 - wydaje się logiczne

No ja jednak myślę, że zwiększanie ColumnCount podczas dodawania wierszy jest właśnie skrajnie nielogiczne, ale mniejsza z tym.

Tylko problem jest taki, że takie rozwiązanie zadziała tylko raz. Po wykonaniu pierwszy raz akcji Dodaj kolumnę faktycznie pojawią się 2 wiersze, ale gdy wykonam kolejny raz akcję model tak jakby się resetował i przy wejściu do metody POST nie zawierał ColumnSize=2 tylko dalej wartość defaultową równą =1.

No tak, bo w żaden sposób nie przechowujesz ColumnCount po stronie widoku. Możesz do tego użyć np. Html.HiddenFor, i wtedy licznik wierszy zwany ColumnCount się nie zresetuje, bo wartość z forma zostanie przesłana w viewmodelu do akcji kontrolera.

  1. W widoku zakomentowałem pewny kod. Mój cel : chcę w zależności od utworzonej liczby wierszy Login / Hasło utworzyć odpowiednią liczbę modeli i dodać ją do listy w ViewModelu (ehh, ciężki mi to opisać). Inaczej : każdy utworzony wiersz w widoku to jeden model. Powiedzmy, że po wykonnaiu 3 x akcji Dodaj kolumnę i uzupełnieniu pól (mam 3 modele) i chcę je dodać do listy by powiedzmy w metodzie About POST mieć listę 3 użytkowników (z uzupełnionymi danymi )

Jak dla mnie, to powinieneś te nowe wiersze dodawać całkowicie po stronie klienckiej. Jaki jest sens wysyłania żądania na serwer, po to tylko, aby w odpowiedzi dostać trochę więcej HTMLa w przeglądarce?

1

@amator963: Wystarczy odpowiednio ustawić atrybut name w input, aby framework po stronie serwera mógł to potem odczytać. Dla pierwszego elementu masz wygenerowane name z indeksem [0] wewnątrz, a Ty dodając nowe wiersze musisz ten indeks zwiększać.

0

Poradziłem sobie z tym hiddenFieldem. Ale mam problem z powiązaniem modeli z listą Users. W przypadku gdy mam taki kod:

  @for (int i = 0; i < Model.ColumnCount; i++)
            {
                @Html.HiddenFor(x=>x.ColumnCount,new{Value=Model.ColumnCount})
                <tr>
                    <td>@Html.TextBoxFor(x => x.Users[i].Login)</td>
                    <td>@Html.TextBoxFor(x => x.Users[i].Password)</td>
                </tr>
            }

To przy : @html.TextBoxFor(x => x.Users[i].Login)</td> otrzymuję błąd "'Indeks był spoza zakresu. Musi mieć wartość nieujemną i mniejszą niż rozmiar kolekcji.
Nazwa parametru: indexx "". Przy pierwszym obrocie pętli for Users posiada rozmiar = 0. Teraz nie wiem jak powiązać te 2 TextBoxFor z listą Users i jak je dodawać :(

1

Jeśli operujesz pętlą for na jakiejś kolekcji, to w warunku pętli powinieneś używać rozmiaru tej kolekcji, a nie jakiejś liczby z czapy. Jak możesz chcieć wyświetlić dane z Users[0], skoro tam niczego nie ma?

0

Dobra poddaje się. Może się nie zrozumieliśmy. Przedstawię jeszcze raz zamysł : Użytkownik niekoniecznie musi działać w sposób synchroniczny : czyli początkowo : uzupełnia login i/lub hasło i wykonuje akcję dodaj wiersz (wtedy dodawałbym użytkownika do Users). Lecz może powiedzmy wywołać 3 razy pod rząd akcję dodaj wiersz : wtedy finalnie powstaje 4 wiersze. Użytkownik w tym momencie uzupełnia dane wszystkich textoboxów i wywołuję akcję dodaj wiersz. Program widząc, że pola dla 4 wierszy są uzupełnione chce je zamienić w 4 modele, a te modele dodać do listy Users i tak dalej

Swoje testy przeprowadzałem w następujący sposób:
Klikałem 1raz dodaj wiersz. Przez co wygenerowały mi się 2 wiersze. Wtedy je uzupełniałem i chciałem mieć w liście Users wprowadzone 2 modele

1

Nieważne jak działa użytkownik, i tak nie można odwołać się do nieistniejącego elementu kolekcji. To, że ColumnCount ma wartość 4, to nie znaczy, że w Users są 4 elementy. A jeśli Users jest puste, to nie możesz się odwołać nawet do zerowego elementu tej kolekcji, więc nawet odwołanie do x.Users[i].Login, gdzie i jest równe 0 nie zadziała.

Ja rozumiem, co Ty chcesz zrobić. I tak się nawet da, tylko nie wystarczy, że w public ActionResult About(UserViewModel model) zwiększysz ColumnCount. Ty tam musisz dodać nowy obiekt do kolekcji Users. A to ColumnCount to tak właściwie, w ogóle nawet żadnego sensu nie ma, do niczego nie jest przydatne.

0

Okej faktycznie głupoty wcześniej pisałem. Ale siedziałem nad tym problemem dużo czasu i w głowie mi się zaczęło mieszać od tego. Racja parametr ColumnCount w zupełności nie jest potrzebny. Dzięki za pomoc i przede wszystkim za wyrozumiałość. Temat dziś rozwiązałem w kilka minut :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1