3

I have the following 2 entities:

 public class Product
{
    [Key]
    public int ID { get; set; }
    [Required]
    public string Name { get; set; }
    public virtual Category Category { get; set; }
}
public class Category
{
    [Key]
    public int ID { get; set; }
    [Required]
    public string Name { get; set; }
    public ICollection<Product> Products { get; set; }
}

and a view model

public class ProductCreateOrEditViewModel
{
    public Product Product { get; set; }
    public IEnumerable<Category> Categories { get; set; }
}

The create view for Product uses this ViewModel. The category ID is set as follows in the view:

<div class="editor-field">
@Html.DropDownListFor(model => model.Product.Category.ID,new SelectList   
(Model.Categories,"ID","Name"))
    @Html.ValidationMessageFor(model => model.Product.Category.ID)
</div>

When the form posts I get an instance of the view model with a product and the selected category object set but since the "Name" property of Category has a [Required] attribute the ModelState is not valid.

As far as creating a Product goes I don't need or care for the "Name" property. How can I get model binding to work such that this is not reported as a ModelState error?

1 Answer 1

4

You should create a correct ViewModel for your View.

The best approach imo is not to expose your domain entities to the view.

You should do a simple DTO flattening from your entities to your viewmodel.

A class like that

public class ProductViewModel
{
   public int ID { get; set; }
   [Required]
   public string Name { get; set; }
   public int CategoryId? { get; set; }
   public SelectList Categories { get; set; }
}

From your controller you map the product to your viewmodel

public ViewResult MyAction(int id)
{
   Product model = repository.Get(id);

   //check if not null etc. etc.

   var viewModel = new ProductViewModel();
   viewModel.Name = model.Name;
   viewModel.CategoryId = model.Category.Id;
   viewModel.Categories = new SelectList(categoriesRepo.GetAll(), "Id", "Name", viewModel.CategoryId)

   return View(viewModel);
}

Then in the action that respond to the post, you map back your viewModel to the product

[HttpPost]
public ViewResult MyAction(ProductViewModel viewModel)
{
   //do the inverse mapping and save the product
}

I hope you get the idea

Sign up to request clarification or add additional context in comments.

2 Comments

Makes sense, the only side effect is that you would have to duplicate model validation on all view model classes wouldn't you or is there a recommended approach for this?
You will have two validation layer in the end. One "client-side" via viewmodel and one "server-side" via your business logic layer as it should be

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.