MVC Strongly-typed object view->controller

MVC Strongly-typed object view->controller
MI
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:39
0

Witam,
Jestem w trakcie nauki asp.net MVC5, postanowiłem stworzyć prostą stronę, lecz..
Mam problem z przekazaniem silnie typowanego obiektu z widoku do kontrolera.
Mój kod:

Model:

Kopiuj
namespace MvcKursVS13.Models
{
    public class Film
    {
        public Film()
        {
            Recenzje = new List<Recenzja>();
        }

        public int FilmId { get; set; }

        [Display(Name = "Tytuł (PL)")]
        public string TytulPl { get; set; }

        [Display(Name = "Tytuł (EN)")]
        public string TytulEn { get; set; }

        [Display(Name = "Data premiery (Polska)")]
        public DateTime DataPremieryPl { get; set; }

        [Display(Name = "Data premiery (Świat)")]
        public DateTime DataPremierySwiat { get; set; }

        [Display(Name = "Gatunek")]
        public string Gatunek { get; set; }

        [Display(Name = "Reżyser")]
        public string Rezyser { get; set; }

        virtual public ICollection<Recenzja> Recenzje { get; set; }

        
    }
}

Controller:

Kopiuj
public async Task<ActionResult> Reviews(int id)
        {
            var film = await db.Filmy.FindAsync(id);
            return View(film);
        }

        [HttpPost]
        public async Task<ActionResult> Reviews(Film filmEntity)//, string textboxik)
        {
            var movie = await db.Filmy.FindAsync(filmEntity.FilmId);
           // movie.Recenzje.Add(new Recenzja { DataNapisania = DateTime.Now, WlasciwaRecenzja = textboxik, Autor = User.Identity.Name });

            await db.SaveChangesAsync();
            ModelState.Clear();

            return View(movie);

        }

View:

Kopiuj
@model MvcKursVS13.Models.Film

    @{
        ViewBag.Title = "Film" + Model.TytulPl;
      //  Layout = "_Layout.cs";
    }

    <h2>title</h2>

    <tr>Wprowadź własną recenzję:</tr>

@using (Html.BeginForm())
    {
 //   <input type="text" name="textboxik" style="width:550px;height:100px;" />
     <input type="submit" value="Dodaj!" />
    }

Nie jest to pełny kod, męczę się z tym już od jakiegoś czasu, więc pousuwałem niepotrzebne rzeczy.

Co jest problemem?

W pierwszym kontrolerze public async Task<ActionResult> Reviews(int id) __poprawnie __pobiera z bazy danych obiekt Film o danym id. Następnie __poprawnie __przekazuje go do widoku. Problem zaczyna się przy drugim kontrolerze ( public async Task<ActionResult> Reviews(Film filmEntity) - obiekt Film przekazany do niego jest zawsze nullem. Nie mam pojęcia dlaczego tak się dzieje - powinien przecież zostać przypisany obiekt silnie typowany z widoku.

Czy spotkał się ktoś może z czymś takim i wie co powinienem w tej sytuacji zrobić?

Z góry wielkie dzięki za odpowiedzi,
M.

n0name_l
  • Rejestracja:ponad 12 lat
  • Ostatnio:prawie 5 lat
  • Postów:2412
1

Ekspertem nie jestem, ale ja bym nie tworzyl obiektu nie majac z czego go stworzyc.

Wrzuc przed submit @Html.EditorForModel() i wyslij i sprawdz czy sie tworzy.

Poza tym:
#Protip: uzywaj narzedzi developerskich wbudowanych w wiekszosci przegladarek, debuggerow http i wstawiaj wiecej informacji na przyszlosc.

Edit:

Zrobilem maly test, dla kodu:

Kopiuj
namespace Test.Models {
    public class SimpleModel {
        public SimpleModel() {
            Ints = new List<int>();
        }

        public int Id { get; set; }
        public string Name { get; set; }
        public string LastName { get; set; }
        public virtual ICollection<int> Ints { get; set; }
    }
}

namespace Test.Controllers {
    public class TestController : Controller {
        [HttpGet]
        public ActionResult Something() {
            return View();
        }

        [HttpPost]
        public ActionResult Something(SimpleModel model) {
            if (model != null) return View("DisplayModel", model);

            return View();
        }
    }
}

// Something.cshtml
@model Test.Models.SimpleModel

@using(Html.BeginForm()) {
    @Html.EditorForModel()
    <input type="submit" value="Click!"/>
}

// DisplayModel.cshtml
@model Test.Models.SimpleModel

<span>@Model.Id</span>
<span>@Model.Name</span>
<span>@Model.LastName</span>

Wszystko dziala.

edytowany 2x, ostatnio: n0name_l
MI
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:39
0

Faktycznie masz rację, po dodaniu @html.EditorForModel() i wypełnieniu przekazany obiekt nie jest nullem.

Przedtem (błędnie) myślałem, że obiekt przechodzi tak: kontroler -> widok -> kontroler, a używając jakichś EditorFor'ów tylko zmieniamy poszczególne pola tego obiektu. A tutaj okazuje się, że tworzy się nowy obiekt.

Z tego wynika, że aby przekazać obiekt z pierwszego kontrolera do widoku, ale też do drugiego kontrolera, trzeba by użyć @html.HiddenFor na kazdym jego polu.

Tak czy siak, wielkie dzięki n0name_l za uświadomienie mnie :)

Pozdrawiam,
M.

somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:29 minut
  • Lokalizacja:Wrocław
1
Mikey napisał(a):

Przedtem (błędnie) myślałem, że obiekt przechodzi tak: kontroler -> widok -> kontroler, a używając jakichś EditorFor'ów tylko zmieniamy poszczególne pola tego obiektu. A tutaj okazuje się, że tworzy się nowy obiekt.

Obiekt to coś, co siedzi w pamięci. Kontroler pracuje na serwerze. Widok zaś jest wyświetlany w przeglądarce, która może być oddalona od serwera o tysiące kilometrów. Jak chciałbyś trzymać referencje do obiektu na taką odległość? ;)

Między widokiem a kontrolerem znajduje się pewna magiczna cześć - model binder. On parsuje żądanie HTTP przysłane przez przeglądarkę i buduje z niego obiekt modelu przypisując poszczególnym właściwościom obiektu, odpowiadające wartości żądania. W najprostszej wersji łączy jest wg wartości atrybutu name danego inputa.

Z tego wynika, że aby przekazać obiekt z pierwszego kontrolera do widoku, ale też do drugiego kontrolera, trzeba by użyć @html.HiddenFor na kazdym jego polu.

Niekoniecznie. Wystarczy przesłać jakiś jego identyfikator, a następnie odczytać ten obiekt z bazy danych czy jakiegoś innego cache.

ŁF
Cóż, znamy takie coś, co trzyma 'referencje" do obiektu "na odległość" - obrzydliwy i paskudny VIEWSTATE i jego koledzy z ASP. Ten, kto to wymyślił, powinien dostać solidnego kopniaka, niemniej jest takie coś. Znamy też inne rozwiązanie, pozwalające na trzymanie takich referencji - sesja.
somekind
Sesja to przecież cache po stronie serwera. A viewstate to zserializowany obiekt po stronie klienta, po powrocie na serwer może mieć taką samą wartość, ale nie referencję.
ŁF
"Referencja" - somekind, zauważyłeś cudzysłów? Umiesz go zinterpetować? To zrób to. Viewstate spełnia założenia autora wątku, więc jako ciekawostkę dodałem to.
somekind
Nie użyłbym nigdy cudzysłowu, żeby przy jego pomocy nazwać wartość referencją... Na szczęście w MVC takich ciekawostek jak viewstate nie ma, ale może lepiej o nim nie mówić, żeby ktoś go czasem sobie nie dorobił. ;P
ŁF
Wiesz dobrze o co chodzi, od czepiania się o słówka jestem ja :P Zwłaszcza jak mam zły humor.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.