I have a code first database. The tables causing me issues are
public class Deadline
{
public Deadline() { }
public Deadline(Year year)
{
this.Year = year;
}
public Guid Id { get; set; }
[Required]
public Year Year { get; set; }
[Required]
[DataType(DataType.Date)]
[DataAnnotationsExtensions.Date]
public DateTime From { get; set; }
[Required]
[DataType(DataType.Date)]
[DataAnnotationsExtensions.Date]
public DateTime To { get; set; }
}
public class Year
{
public Year(Int32 value) { this.Value = value; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[Required]
[Display(Name="Year")]
[DataAnnotationsExtensions.Year]
public Int32 Value { get; set; }
public virtual Deadline Deadline { get; set; }
}
I have a controller for the year which has the create get method
public ActionResult Create()
{
ViewBag.Id = new SelectList(db.Deadline, "Id", "Id");
var Year = new Year(db.Year.OrderByDescending(x => x.Value).FirstOrDefault().Value + 1);
Year.Deadline = new Deadline(Year);
return View(Year);
}
and the post method
[HttpPost]
public ActionResult Create(Year year)
{
if (ModelState.IsValid)
{
if (db.Year.Select(x => x.Value).Contains(year.Value))
{
ModelState.AddModelError("AlreadyExists", "That year has already been added to the database, please edit instead");
return View(year);
}
db.Year.Add(year);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(year);
}
Whenever i try to create a new Year though (having set the year value and the deadline dates) it gets back to the post and the modelstate is invalid and says
The Year field is required.
I’ve checked and at the post method year.Deadline.Year is indeed null, what i want to know is why (and how i can fix this) it’s been driving me crazy, even putting
@{ Model.Deadline.Year = Model; }
in the view did nothing.
Edit The following works
using (ProjectSelectionAndAllocation.Models.DatabaseContext Context = new Models.DatabaseContext())
{
var h = new Models.Year(1001);
h.Deadline = new Models.Deadline(h);
Context.Year.Add(h);
Context.SaveChanges();
}
Ok, I have two basic theories as to the problem but one solution that you should implement anyway.
So I’ll start with the solution: Use a ViewModel. That is a small, flat, lightweight class that just contains the fields the view wants to display. It should not be the same as your data layer. The view should just be rendering stuff, and the ViewModel should just contain the data to be displayed.
You’ll map the data from your actual objects to the ViewModel (AutoMapper is pretty popular for this.)
Now, why will this help? Well, my theories as to your problem are:
1) The model binder might not like virtual properties. Whenever you return a form to a function in MVC there’s little bit of code that runs through and tries to map all the values in the form to their fields. This model binder might be unable to figure out a virtual field or there might be some good reason not to automatically map to one. Either way, a ViewModel would replace this with a regular public property.
2) The other possibility, but I’m not too sure of it, is that there is a circular reference (Year -> Deadline -> Year ) and the model binder doesn’t handle that well. Again though, the ViewModel concept would get rid of that and you’d just have a Year field in the ViewModel.