0

I am using code-first with EF. Validation seems to be failing on a dropdown list with the error System.NullReferenceException: Object reference not set to an instance of an object. This happens when I save a record and I intentionally leave controls empty to test the validation. It happens even if the dropdown list itself has a selection.

here is part of my view:

<div class="editor">
    @Html.LabelFor(model => model.EmployeeID)
    @Html.DropDownListFor(model => model.EmployeeID, new SelectList(Model.Employees, "Value", "Text"))
    @Html.ValidationMessageFor(model => model.EmployeeID)
</div>

If I use a textbox validation works:

<div class="editor">
    @Html.LabelFor(model => model.EmployeeID)
    @Html.TextBoxFor(model => model.EmployeeID, new { style = "width: 250px;" })
    @Html.ValidationMessageFor(model => model.EmployeeID)
</div>

here are my Create controller actions:

    public ActionResult Create()
{
    var e = iEmployeeRepository.GetAll();
    var visitorLogViewModel = new VisitorLogViewModel
    {
        Employees = e.Select(x => new SelectListItem
        {
            Value = x.EmployeeID,
            Text = x.EmployeeName
        })
    };
    return View(visitorLogViewModel);
}

//
// POST: /VisitorLogs/Create

[HttpPost]
public ActionResult Create(VisitorLog visitorlog)
{
    if (ModelState.IsValid) {
        iVisitorlogRepository.Add(visitorlog);
        iVisitorlogRepository.Save();
        return RedirectToAction("Search");
    } else {
        return View();
    }
}

And my viewmodel:

    public class VisitorLogViewModel
{
    public int Id { get; set; }

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

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

    [Required(ErrorMessage = "Employee ID is required.")]
    [Display(Name = "GB Employee")]
    public string EmployeeID { get; set; }

    [Display(Name = "Visit Reason")]
    public string VisitReason { get; set; }

    [Display(Name = "Time In")]
    public DateTime TimeIn { get; set; }

    [Display(Name = "Time Out")]
    public DateTime TimeOut { get; set; }

    [Display(Name = "GB Employee")]
    public string EmployeeName { get; set; }

    public IEnumerable Employees { get; set; }
    public VisitorLog VisitorLog { get; set; }
}

And my partial model for validation:

    [MetadataType(typeof(VisitorLogMetaData))]
public partial class VisitorLog
{

}

public class VisitorLogMetaData
{
    [Required(ErrorMessage = "Visitor name is required.")]
    [MaxLength(128)]
    public string VisitorName { get; set; }

    [Required(ErrorMessage = "Company name is required.")]
    [MaxLength(128)]
    public string CompanyName { get; set; }

    [Required(ErrorMessage = "GB Employee is required.")]
    [MaxLength(128)]
    public string EmployeeID { get; set; }

    [Required(ErrorMessage = "Visit reason is required.")]
    [MaxLength(254)]
    public string VisitReason { get; set; }

    [Required(ErrorMessage = "Time in is required.")]
    public DateTime TimeIn { get; set; }

    [Required(ErrorMessage = "Time out reason is required.")]
    public DateTime TimeOut { get; set; }
}

And finally my model:

    public partial class VisitorLog
{
    public int Id { get; set; }
    public string VisitorName { get; set; }
    public DateTime TimeIn { get; set; }
    public DateTime TimeOut { get; set; }
    public string CompanyName { get; set; }
    public string EmployeeID { get; set; }
    public string VisitReason { get; set; }

    // Navigation properties
    [ForeignKey("EmployeeID")]
    public virtual Employee Employee { get; set; }
}

I read there was a bug in MVC razor regarding the DropDownListFor but I don't know if that applies in my situation. I have tried some of the solutions and they didn't work for me. I am using 4.5 framework.

Thanks.

Edit:

One thing I noticed, when I submit the page and the error stops on the dropdown element:

@Html.DropDownListFor(model => model.EmployeeID, new SelectList(Model.Employees, "Value", "Text"))

the Model in Model.Employees is null, like it is loosing its binding when the page is submited.

steveareeno
  • 1,925
  • 5
  • 39
  • 59

1 Answers1

0

Ok, I did some fundemental changes to my classes. First, I changed the post method in my controller. Previously I was passing the model to the post, now I am passing the view model and mapping it to the model before saving via my repository:

    //
    // POST: /VisitorLogs/Create

    [HttpPost]
    public ActionResult Create(VisitorLogViewModel visitorLogViewModel)
    {
        var e = iEmployeeRepository.GetAll();
        VisitorLog visitorLog = new VisitorLog();
        visitorLog.Id = visitorLogViewModel.Id;
        visitorLog.VisitorName = visitorLogViewModel.VisitorName;
        visitorLog.CompanyName = visitorLogViewModel.CompanyName;
        visitorLog.EmployeeID = visitorLogViewModel.EmployeeID;
        visitorLog.TimeIn = visitorLogViewModel.TimeIn;
        visitorLog.TimeOut = visitorLogViewModel.TimeOut;
        visitorLog.VisitReason = visitorLogViewModel.VisitReason;
        visitorLogViewModel.Employees = new SelectList(e, "EmployeeID", "EmployeeName");

        if (ModelState.IsValid)
        {
            iVisitorlogRepository.Add(visitorLog);
            iVisitorlogRepository.Save();
            return RedirectToAction("Search");
        } else {
            return View(visitorLogViewModel);
        }
    }

Next, I had to add the "required" attribute (validation) to the viewmodel:

    public class VisitorLogViewModel
{
    public int Id { get; set; }

    [Required(ErrorMessage = "Visitor name is required.")]
    [MaxLength(128)]
    [Display(Name = "Visitor Name")]
    public string VisitorName { get; set; }

    [Required(ErrorMessage = "Company name is required.")]
    [MaxLength(128)]
    [Display(Name = "Company Name")]
    public string CompanyName { get; set; }

    [Required(ErrorMessage = "GB Employee is required.")]
    [MaxLength(16)]
    [Display(Name = "GB Employee")]
    public string EmployeeID { get; set; }

    [Required(ErrorMessage = "Visit Reason is required.")]
    [MaxLength(254)]
    [Display(Name = "Visit Reason")]
    public string VisitReason { get; set; }

    [Display(Name = "Time In")]
    public DateTime TimeIn { get; set; }

    [Display(Name = "Time Out")]
    public DateTime TimeOut { get; set; }

    [Display(Name = "GB Employee")]
    public string EmployeeName { get; set; }

    public SelectList Employees { get; set; }
}

Not sure if that is the most effcient method but everything works now. If someone sees something wrong with this method let me know.

steveareeno
  • 1,925
  • 5
  • 39
  • 59