views:

1149

answers:

1

I have problem with model binding in my ASP.NET MVC 2 RC application that uses NHibernate for data access. We are trying to build the application in a Ruby on Rails way and have a very simple architecture where the domain entities are used all the way from the database to the view.

The application has a couple of domain entities which 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 the view that renders the edit form has the following statement to display a dropdown list:

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

Please disregard the use of "non-typed" view data to hold the category collection.

The action method that receives the form post is similar to to the following. Note that the TransactionFilter attribute adds NHibernate transaction handling and commits the transaction if no exceptions occur and validation succeeds.

[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 issue is that the product.Category.Id is set with the value selected in the form, e.g. Category.Id = "2". Using the default model binder results in the following type of NHibernate exception:

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

That makes a lot of sense since the product already has a category assigned and only the primary key of that existing category is being changed. Another category instance should have been assigned instead.

I guess a custom ModelBinder can be created to handle this issue but is there a simpler way to make this work? Can (and should) the domain entities be modified to handle this?

A: 

I've used similar techniques with Linq to SQL classes before with no problems. I don't think you'd need a custom ModelBinder for this. UpdateModel should be updating the Product class you are passing into it - not the Category sub-class attached to it. Check the html generated by the DropDownListFor helper. What is the name of the element? It should be the name of the foreign-key field in your Products table (e.g. "CategoryID" or "Product.CategoryID" not "Category.Id"). If it's "Category.Id" - try changing the first parameter of the DropDownListFor to either "model => model.Category" or "model => model.CategoryID" (or whatever the foreign key field is). This should cause UpdateModel to only update the foreign-key field in the Product class and not the Category class ID.

Bryan
The foreign key is not available as the relationship is handled entirely by NHibernate. I ended up just handling the category case manually and not creating a custom model binder
HakonB