0

I am attempting to create a new database record in ASP.NET MVC using a code first database. I created the controller for the model with scaffolding and views and every time I attempt to do a POST on create the model is never valid, the two models that are members of the one I am attempting to create are always null.

Below is the code for the Create POST in my controller.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "DeviceNumber,ManufacturerNumber,CarrierNumber,Name")] Device device)
{
    if (ModelState.IsValid)
    {
        db.Devices.Add(device);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    ViewBag.CarrierNumber = new SelectList(db.Carriers, "CarrierNumber", "CarrierID", device.CarrierNumber);
    ViewBag.ManufacturerNumber = new SelectList(db.Manufacturers, "ManufacturerNumber", "ManufacturerID", device.ManufacturerNumber);
    return View(device);
}

The following is the model I am trying to create. The errors in the ModelState always tell me that Manufacturer and Carrier are null, but they are required.

public class Device
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int DeviceNumber { get; set; }

    [ForeignKey("ManufacturerNumber"), Required]
    public virtual Manufacturer Manufacturer { get; set; }

    public int ManufacturerNumber { get; set; }

    [ForeignKey("CarrierNumber")]
    [Required]
    public virtual Carrier Carrier { get; set; }

    public int CarrierNumber { get; set; }

    [Required]
    public string Name { get; set; }
}

I've tried assigning the Carrier and Manufacturer in the Create method but it still resulted in a failed validation.

Erik
  • 40
  • 1
  • 4

1 Answers1

0

You should not have the [Required] attributes on the navigation properties(Manufacturer and Carrier).

Instead keep it on the ManufacturerNumber and CarrierNumber properties because you are getting values for those columns from the UI.

public class Device
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int DeviceNumber { get; set; }

    [ForeignKey("ManufacturerNumber")]
    public virtual Manufacturer Manufacturer { get; set; }

    [Required]
    public int ManufacturerNumber { get; set; }

    [ForeignKey("CarrierNumber")]   
    public virtual Carrier Carrier { get; set; }

    [Required]
    public int CarrierNumber { get; set; }

    [Required]
    public string Name { get; set; }
}

Also there is no need to include DeviceNumber inside Bind as the value for this will be auto generated. This should be good enough.

[HttpPost]
public ActionResult Create([Bind(Include = "ManufacturerNumber,CarrierNumber,Name")]
                                                                              Device device)
{
 // your code
}

This should work fine assuming your view has a form which sends data for the required fields.

@model Device
@using (Html.BeginForm())
{
    @Html.ValidationSummary(false)

    @Html.LabelFor(f=>f.Name)
    @Html.TextBoxFor(f=>f.Name)

    @Html.LabelFor(f => f.CarrierNumber)
    @Html.DropDownList("CarrierNumber")

    @Html.LabelFor(f => f.ManufacturerNumber)
    @Html.DropDownList("ManufacturerNumber")

    <input type="submit"/>
}

Remember, the best way to prevent over posting is to use a view model. This also helps to keep your layers loosely coupled.

Community
  • 1
  • 1
Shyju
  • 214,206
  • 104
  • 411
  • 497