3

I have the following model:

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

  [Required]
  [StringLength(10)]
  public string ProductCode { get; set; }

  [Required]
  [StringLength(40)]
  public string ProductName { get; set; }
}

and the following pair of Add methods in the controller:

[HttpGet]
public ActionResult Add()
{
  return View();
}

[HttpPost]
[ValidateInput(false)]
[ValidateAntiForgeryToken]
public ActionResult Add(Product product)
{
  productRepository.Add(product);

  return RedirectToAction("Index");
}

This is the Add view:

@using Models
@model Product

<h2>Add Product</h2>

@using (@Html.BeginForm("Add", "Home")) {
  @Html.AntiForgeryToken()
  @Html.EditorForModel()
  <input type="submit" id="btnSubmit" value="Submit"/>
}

Everything is displayed just fine, unfortunately I am unable to submit the form. It took me a while to figure out that the Id field gets validated. Indeed, if I remove the HiddenInput attribute, I can see on submitting that it tells me the Id field is required.

Is there a way to mark it as not required while still using EditorForModel()?

Marcel Popescu
  • 3,146
  • 3
  • 35
  • 42

2 Answers2

7

If you must keep the primary key as part of the model, then you need to override the default for DataAnnotationsModelValidatorProvider that value types are required. Add the following to the Application_Start method in Global.asax.cs:

ModelValidatorProviders.Providers.Clear(); 
ModelValidatorProviders.Providers.Add(new DataAnnotationsModelValidatorProvider());
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
counsellorben
  • 10,924
  • 3
  • 40
  • 38
  • Yes yes YES! Thank you... just one thing, you have a typo (I believe), it's `DataAnnotationsModelValidatorProvider` - you have an additional "Metadata" in there. – Marcel Popescu Sep 06 '11 at 15:42
  • Oops! You are correct. I updated my answer to correct my brain lock (fingers typing, brain otherwise engaged). – counsellorben Sep 06 '11 at 15:44
1

You should consider using view models instead of sending your domain entities as models to views.

public class ProductAddModel
{
  [Required]
  [StringLength(10)]
  public string ProductCode { get; set; }

  [Required]
  [StringLength(40)]
  public string ProductName { get; set; }
}

Then use a tool like AutoMapper to map the viewmodel back to your domain model

[HttpPost]
[ValidateInput(false)]
[ValidateAntiForgeryToken]
public ActionResult Add(ProductAddModel productAddModel)
{
  if (ModelState.IsValid)
  {
      Product product = Mapper.Map<ProductAddModel, Product>(productAddModel);

      productRepository.Add(product);
  }

  return RedirectToAction("Index");
}
Eranga
  • 32,181
  • 5
  • 97
  • 96
  • It's not a domain entity, it's just a DTO... I'm of the principle that models serve the views, so that's a view model. The problem is that the Edit method would need the Id, so that means two view models, which I'm reluctant to do. (I'll do it if there's no other way, but I was hoping there was actually a way to tell it not to check the Id at least in some cases.) – Marcel Popescu Sep 06 '11 at 14:21
  • @Marcel since the `ID` property is a value type MVC will add required validation. You can make it nullable `public int? Id { get; set; }` since you say that `Product` is a DTO. – Eranga Sep 06 '11 at 14:57
  • I don't think EF will let me make the primary key nullable... and even if it does, I don't like the idea. So I guess the answer to my question is "no", then? – Marcel Popescu Sep 06 '11 at 15:29