LiamB
LiamB

Reputation: 18606

Asp.Net MVC - Binding of parameter to model value!

This seems like the model binding is causing me issues.

Essentially I have a model called ProductOption and for the purpose of this question it has 2 fields

ID (Int) PK ProductID (Int) FK

I have a standard route set-up

    context.MapRoute(
        "Product_default",
        "Product/{controller}/{action}/{id}",
        new { controller = "Product", action = "Index", id = UrlParameter.Optional }
    );

and if the user wants to add an option the URL is,

/Product/Options/Add/1

in the above URL 1 is the ProductID, I have the following code to return a blank model the the view,

[HttpGet]
public ActionResult Add(int id)
{
    return View("Manage", new ProductOptionModel() { ProductID = id });
}

Now in my view I keep a hidden field

<%= Html.HiddenFor(x=>x.ID) %>

This is used to determine (on submit) if we are editing or adding a new option. However the Model binder in .net seems to replace .ID (Which was 0 when leaving the above get actionresult) with 1 (or the value of the id parameter in the URL)

How can I stop or work around this?

ViewModel

public class ProductExtraModel
{
    //Database 
    public int ID { get; set; }
    public string Name { get; set; }
    public int ProductID { get; set; }

    public ProductModel Product { get; set; }
}

Upvotes: 2

Views: 2384

Answers (2)

Amadiere
Amadiere

Reputation: 11426

I think the id parameter is being set because by default, your route sets it. Inside your controller, you're setting an additional parameter in your ViewModel of ProductID, this will probably always equal the ID parameter as both are basically being set to the QueryString / GET parameter. (1 in this case).

Your fix of changing the route works as you stop it from allocating the ID parameter seems a good choice, but maybe not ideal - depending on how you want to solve the issue:

context.MapRoute(
  "Product_addoptionalextra",
  "Product/{controller}/Add/{ProductID}",
  new { controller = "Product",action="Add", ProductID = UrlParameter.Optional }
);

Alternatively, re-arrange your variables so that ID is actually the relevant ProductID, then you could have OtherID which represents ID.

The way I would maybe suggest to fix this problem if you have MVC 2, is to use EditorTemplates / DisplayTemplates. Although I don't know your ProductViewModel, I assume it has the ID within it. If you set the appropriate template, you can almost forget about the potentially overlapping IDs.

public class ProductExtraModel
{
   //Database 
  public int ID { get; set; }
  public string Name { get; set; }

  [UIHint("Product")]
  public ProductModel Product { get; set; }
}

You'll be able to access the product ID when the model is passed back into the controller using productExtraViewModel.Product.ID and your normal ID will still be available on productViewModel.Id.

Upvotes: 2

LiamB
LiamB

Reputation: 18606

I've fixed this by updating my routes (Not completley happy with it - but it works)

 public override void RegisterArea(AreaRegistrationContext context)
        {
            context.MapRoute(
               "Product_addoptionalextra",
               "Product/{controller}/Add/{ProductID}",
               new { controller = "Product",action="Add", ProductID = UrlParameter.Optional }
            );

            context.MapRoute(
                "Product_default",
                "Product/{controller}/{action}/{id}",
                new { controller = "Product", action = "Index", id = UrlParameter.Optional }
            );         
        }

Upvotes: 0

Related Questions