views:

28

answers:

1

I'm building an Asp.net MVC 2 application.

I have an entity called Team that is mapped via public properties to two other entities called Gender and Grade.

public class Team
{
    public virtual int Id { get; private set; } 
    public virtual string CoachesName { get; set; } 
    public virtual string PrimaryPhone { get; set; } 
    public virtual string SecondaryPhone { get; set; }
    public virtual string EmailAddress { get; set; } 
    public virtual Grade Grade { get; set; } 
    public virtual  Gender Gender { get; set; } 
}

I have a ViewModel that looks like this.

public class TeamFormViewModel
{

    public TeamFormViewModel()
    {
        Team = new Team();
        Grade = new SelectList((new Repository<Grade>()).GetList(),"ID", "Name",Team.Grade);
        Gender = new SelectList((new Repository<Gender>()).GetList(), "ID", "Name", Team.Gender);
    }

    public Team Team { get; set; }
    public virtual SelectList Grade { get; set; }
    public virtual SelectList Gender { get; set; }
}

My form renders as I would expect. When I debug the Create method I see that the Gender and Grade properties are NULL on my Team object.

    [HttpPost, Authorize]
    public ActionResult Create(Team team)
    {
        try
        {
            if (ModelState.IsValid)
            {
                (new Repository<Team>()).Save(team);

            }
            return RedirectToAction("Index");
        }
        catch
        {
            return View();
        }
    }

What am I doing wrong?

Thanks, Eric

+1  A: 

I recommend that you post and bind back to a view model class rather than your entity class. Create an extension method for your view model class that will return your entity class. Here's some working code:

public class Team
{
   public virtual int Id { get; set; }
   public virtual string CoachesName { get; set; }
   public virtual string PrimaryPhone { get; set; }
   public virtual string SecondaryPhone { get; set; }
   public virtual string EmailAddress { get; set; }
   public virtual Grade Grade { get; set; }
   public virtual Gender Gender { get; set; }
}

public class Grade
{
   public virtual int Id { get; set; }
   public virtual string Name { get; set; }
}

public class Gender
{
   public virtual int Id { get; set; }
   public virtual string Name { get; set; }
}

public class TeamFormViewModel
{
   public TeamFormViewModel()
   {
      var gradeList = (new Repository<Grade>()).GetList();
      var genderList = (new Repository<Gender>()).GetList();
      GradeList = new SelectList(gradeList, "Id", "Name");
      GenderList = new SelectList(genderList, "Id", "Name");
   }

   [HiddenInput(DisplayValue = false)]
   public int Id { get; set; }

   [DisplayName("Coach Name")]
   [Required]
   public string CoachesName { get; set; }

   [DisplayName("Primary Phone")]
   [DataType(DataType.PhoneNumber)]
   [Required]
   public string PrimaryPhone { get; set; }

   [DisplayName("Secondary Phone")]
   [DataType(DataType.PhoneNumber)]
   public string SecondaryPhone { get; set; }

   [DisplayName("Email Address")]
   [DataType(DataType.EmailAddress)]
   [Required]
   public string EmailAddress { get; set; }

   [DisplayName("Grade")]
   [Range(1, 5)]
   public int SelectedGradeId { get; set; }

   [DisplayName("Gender")]
   [Range(1, 5)]
   public int SelectedGenderId { get; set; }

   private int selectedGradeId = 0;
   private int selectedGenderId = 0;

   public SelectList GradeList { get; set; }
   public SelectList GenderList { get; set; }
}

public static class TeamExtensions
{
   public static Team ToTeam(this TeamFormViewModel viewModel)
   {
      return new Team
      {
         Id = viewModel.Id,
         CoachesName = viewModel.CoachesName,
         PrimaryPhone = viewModel.PrimaryPhone,
         SecondaryPhone = viewModel.SecondaryPhone,
         EmailAddress = viewModel.EmailAddress,
         Grade = (new Repository<Grade>())
            .GetList()
            .Where(x => x.Id == viewModel.SelectedGradeId)
            .Single(),
         Gender = (new Repository<Gender>())
            .GetList()
            .Where(x => x.Id == viewModel.SelectedGradeId)
            .Single()
      };
   }

   public static TeamFormViewModel ToTeamFormViewModel(this Team team)
   {
      return new TeamFormViewModel
      {
         Id = team.Id,
         CoachesName = team.CoachesName,
         PrimaryPhone = team.PrimaryPhone,
         SecondaryPhone = team.SecondaryPhone,
         EmailAddress = team.EmailAddress,
         SelectedGradeId = team.Grade.Id,
         SelectedGenderId = team.Gender.Id
      };
   }
}

public class TeamController : Controller
{
   public ActionResult Create()
   {
      var viewModel = new TeamFormViewModel();
      return View(viewModel);
   }

   [HttpPost]
   public ActionResult Create(TeamFormViewModel viewModel)
   {
      if (ModelState.IsValid)
      {
         (new Repository<Team>())
            .Save(viewModel.ToTeam());
      }
      return View(viewModel);
   }
}

And finally, the view:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<Stack1.Models.TeamFormViewModel>" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml"&gt;
<head runat="server">
   <title>Create</title>
   <script type="text/javascript" src="/Scripts/jquery-1.4.1.js"></script>
   <script type="text/javascript" src="/Scripts/MicrosoftAjax.js"></script>
   <script type="text/javascript" src="/Scripts/MicrosoftMvcValidation.js"></script>
</head>
<body>
   <% Html.EnableClientValidation(); %>
   <% using (Html.BeginForm()) { %>
   <%= Html.ValidationSummary() %>
   <fieldset>
      <legend>Fields</legend>
      <%= Html.LabelFor(x => x.CoachesName) %>
      <p>
         <%= Html.TextBoxFor(x => x.CoachesName) %>
         <%= Html.ValidationMessageFor(x => x.CoachesName) %>
      </p>

      <%= Html.LabelFor(x => x.PrimaryPhone)%>
      <p>
         <%= Html.EditorFor(x => x.PrimaryPhone) %>
         <%= Html.ValidationMessageFor(x => x.PrimaryPhone)%>
      </p>

      <%= Html.LabelFor(x => x.SecondaryPhone)%>
      <p>
         <%= Html.EditorFor(x => x.SecondaryPhone) %>
         <%= Html.ValidationMessageFor(x => x.SecondaryPhone)%>
      </p>

      <%= Html.LabelFor(x => x.EmailAddress)%>
      <p>
         <%= Html.EditorFor(x => x.EmailAddress) %>
         <%= Html.ValidationMessageFor(x => x.EmailAddress)%>
      </p>

      <%= Html.LabelFor(x => x.SelectedGradeId)%>
      <p>
         <%= Html.DropDownListFor(x => x.SelectedGradeId, Model.GradeList) %>
         <%= Html.ValidationMessageFor(x => x.SelectedGradeId)%>
      </p>

      <%= Html.LabelFor(x => x.SelectedGenderId)%>
      <p>
         <%= Html.DropDownListFor(x => x.SelectedGenderId, Model.GenderList) %>
         <%= Html.ValidationMessageFor(x => x.SelectedGenderId)%>
      </p>
      <p>
         <%= Html.HiddenFor(x => x.Id) %>
         <input type="submit" value="Save" />
      </p>
   </fieldset>
   <% } %>
</body>
</html>
Tyler Jensen
This makes a lot of sense to me. I will give this a try hopefully in a bit.
Eric Neunaber
This worked very well thank you. Is it common practice to put the required logic in the ViewModels? That was one thing I have not read into yet.
Eric Neunaber
No. It's definitely not common practice. I tried to stick to your original a bit, but in reality, I'd refactor out the calls to the respository in the view model constructor. I'd likely have the controller call the repository to get the lists, either from DB or cache, and pass those into the constructor. It's a good idea to make your view models know nothing about your backend and just let them handle the job of providing your view with the data required to render the UI.
Tyler Jensen