Binding ASP.NET MVC 2 RC with NHibernate and Dropdowns

I have a model binding problem in my ASP.NET MVC 2 RC application that uses NHibernate to access data. We are trying to create an application in Ruby on Rails and have a very simple architecture where domain objects are used completely from the database to the view.

An application has a pair of domain objects that can be illustrated by the following two classes:

public class Product { ... public Category Category { get; set; } } public class Category { public int Id { get; set; } public string Name { get; set; } } 

In a view in which the edit form has the following operator for displaying a drop-down list:

 <%= Html.DropDownListFor(model => model.Category.Id, new SelectList(ViewData["categories"] as IList<Category>, "Id", "Name"), "-- Select Category --" ) %> 

Please ignore the use of "unprinted" view data to store a collection of categories.

The action method receiving the form message is similar to the following. Note that the TransactionFilter attribute adds an NHibernate transaction transaction and completes the transaction if no exceptions occur and the validation is successful.

 [HttpPost] [TransactionFilter] public ActionResult Edit(int id, FormCollection collection) { var product = _repository.Load(id); // Update the product except the Id UpdateModel(product, null, null, new[] {"Id"}, collection); if (ModelState.IsValid) { return RedirectToAction("Details", new {id}); } return View(product); } 

My problem is that product.Category.Id is set with the value selected in the form, for example. Category.Id = "2". Using model bindings by default results in an NHibernate exception of the following type:

 identifier of an instance of Name.Space.Entities.Category was altered from 4 to 2 

This makes a lot of sense, since the product already has a category, and only the primary key of this existing category is changed. Instead, another instance of the category should be assigned.

I think a custom ModelBinder can be created to solve this problem, but is there an easier way to make this work? Can (and should) domain objects be modified to handle this?

+4
source share
3 answers

The solution we chose at that time was something similar:

 TryUpdateModel(product, null, null, new[] {"Category"}, collection); int categoryId; if (int.TryParse(collection["Category.Id"], NumberStyles.Integer, CultureInfo.InvariantCulture, out categoryId) && categoryId > 0) { product.Category = _categoryRepository.Load(categoryId); } else { product.Category = null; } 

We simply pass the model binding to eliminate the association property and handle it manually. Not really, but worked at the time ....

0
source

I solved the problem with combo fields on my edit page by changing the following line

 @Html.DropDownListFor(x => x.Employee.Id, new SelectList(ViewBag.Employees, "Id", "DisplayName")) 

by

 @Html.DropDownListFor(x => x.Employee, new SelectList(ViewBag.Employees, "Id", "DisplayName")) 

So, I deleted the ".Id" as suggested by Brian. Before the change, the model contained only the employee identifier, and after the change, Binder also loaded all the employee details into the model.

+2
source

I have already used similar methods with Linq to SQL classes without problems. I do not think that for this you need a special ModelBinder. UpdateModel should update the product class you are transitioning to, not the subclass of the class attached to it. Check out the html generated by the DropDownListFor helper. What is the name of the item? This should be the name of the foreign key field in your product table (for example, "CategoryID" or "Product.CategoryID", not "Category.Id"). If it is "Category.Id" - try changing the first DropDownListFor parameter to "model => model.Category" or "model => model.CategoryID" (or regardless of the foreign key field). This should force UpdateModel to update the foreign key field in the Product class, rather than the category class identifier.

0
source

All Articles