This seems to be a pretty trivial problem that I have been stuck on for about an hour, and I don't understand what I'm doing wrong.
I have a ViewModel:
public class SongFormViewModel
{
public Song Song { get; set; }
public SelectList AlbumList { get; set; }
public SongFormViewModel(Song song, IQueryable<Album> albumList)
{
Song = song;
AlbumList = new SelectList(albumList, "AlbumId", "Title", song.AlbumId);
}
}
I have a Create view:
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<NightSpot.Models.SongFormViewModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Create
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Create</h2>
<%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>
<% using (Html.BeginForm()) {%>
<fieldset>
<legend>Fields</legend>
<p>
<label for="AlbumId">AlbumId:</label>
<%= Html.DropDownList("AlbumId", Model.AlbumList) %>
<%= Html.ValidationMessage("AlbumId", "*") %>
</p>
<p>
<label for="Title">Title:</label>
<%= Html.TextBox("Title") %>
<%= Html.ValidationMessage("Title", "*") %>
</p>
<p>
<label for="TrackNumber">TrackNumber:</label>
<%= Html.TextBox("TrackNumber") %>
<%= Html.ValidationMessage("TrackNumber", "*") %>
</p>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
<div>
<%=Html.ActionLink("Back to List", "Index") %>
</div>
</asp:Content>
And I have a SongsController Create method:
//
// POST: /Songs/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Song song)
{
if (ModelState.IsValid)
{
try
{
repository.Add(song);
repository.Save();
return RedirectToAction("Details", new { id = song.SongId });
}
catch
{
ModelState.AddRuleViolations(song.GetRuleViolations());
}
}
return View(new SongFormViewModel(song, repository.FindAllAlbums()));
}
When I navigate to the /Songs/Create URL I see the expected UI. My drop down list contains a list of all valid Albums in my database (from the Albums table), and I have validated that each AlbumId is the value, and each Title is the Text. Great.
So now when I go to fill out my form and hit "Save", I get an error telling me that I need to select an Album. When I step through the debugger in Visual Web Developer, I see that the Title & Track Number are being populated correctly, but my Album object is NULL, and AlbumId is still 0. Any ideas?
UPDATE
Taking Matt's advice, I updated my SongsController to use a SongFormViewModel instead of a Song. Here's the new controller:
//
// POST: /Songs/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(SongFormViewModel song)
{
Song newSong = song.Song;
newSong.AlbumId = (int) song.AlbumList.SelectedValue;
if (ModelState.IsValid)
{
try
{
repository.Add(newSong);
repository.Save();
return RedirectToAction("Details", new { id = song.Song.SongId });
}
catch
{
ModelState.AddRuleViolations(song.Song.GetRuleViolations());
}
}
return View(new SongFormViewModel(newSong, repository.FindAllAlbums()));
}
I fired up my /Songs/Create URL and got a message saying SongFormViewModel required a parameterless constructor.
So I made one.
When I re-ran the /Songs/Create URL I got an "Object reference not set to an instance of an object" exception on this line in my SongsController:
Line 93: newSong.AlbumId = (int) song.AlbumList.SelectedValue;
Ideas?
UPDATE
OK, so I updated my view per Matt's suggestion of prefixing the fields with "Song":
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<NightSpot.Models.SongFormViewModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Create
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Create</h2>
<%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>
<% using (Html.BeginForm()) {%>
<fieldset>
<legend>Fields</legend>
<p>
<label for="Song.AlbumId">AlbumId:</label>
<%= Html.DropDownList("Song.AlbumId", Model.AlbumList) %>
<%= Html.ValidationMessage("Song.AlbumId", "*") %>
</p>
<p>
<label for="Song.Title">Title:</label>
<%= Html.TextBox("Song.Title") %>
<%= Html.ValidationMessage("Song.Title", "*") %>
</p>
<p>
<label for="Song.TrackNumber">TrackNumber:</label>
<%= Html.TextBox("Song.TrackNumber") %>
<%= Html.ValidationMessage("Song.TrackNumber", "*") %>
</p>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
<div>
<%=Html.ActionLink("Back to List", "Index") %>
</div>
</asp:Content>
The behavior is, unfortunately, the same. Null SelectedValue and an empty Song object.