bind attribute include and exclude property with complex type nested objects

18.7k views Asked by At

Ok, this is weird. I cannot use BindAttribute's Include and Exclude properties with complex type nested objects on ASP.NET MVC.

Here is what I did:

Model:

public class FooViewModel {

    public Enquiry Enquiry { get; set; }
}

public class Enquiry {

    public int EnquiryId { get; set; }
    public string Latitude { get; set; }
}

HTTP POST action:

[ActionName("Foo"), HttpPost]
public ActionResult Foo_post(
    [Bind(Include = "Enquiry.EnquiryId")]
    FooViewModel foo) {

    return View(foo);
}

View:

@using (Html.BeginForm()) {

    @Html.TextBoxFor(m => m.Enquiry.EnquiryId)
    @Html.TextBoxFor(m => m.Enquiry.Latitude)

    <input type="submit" value="push" />
}

Does not work at all. Can I only make this work if I define the BindAttribute for Enquiry class as it is stated here:

How do I use the [Bind(Include="")] attribute on complex nested objects?

2

There are 2 answers

7
Darin Dimitrov On BEST ANSWER

Yes, you can make it work like that:

[Bind(Include = "EnquiryId")]
public class Enquiry 
{
    public int EnquiryId { get; set; }
    public string Latitude { get; set; }
}

and your action:

[ActionName("Foo"), HttpPost]
public ActionResult Foo_post(FooViewModel foo) 
{
    return View(foo);
}

This will include only the EnquiryId in the binding and leave the Latitude null.

This being said, using the Bind attribute is not something that I would recommend you. My recommendation is to use view models. Inside those view models you include only the properties that make sense for this particular view.

So simply readapt your view models:

public class FooViewModel 
{
    public EnquiryViewModel Enquiry { get; set; }
}

public class EnquiryViewModel 
{
    public int EnquiryId { get; set; }
}

There you go. No longer need to worry about binding.

0
vipero07 On

IMHO there is a better way to do this.

Essentially if you have multiple models in the view model the post controller's signature would contain the same models, as opposed to the view model.

I.E.

public class FooViewModel {
    public Bar BarV { get; set; }
    public Enquiry EnquiryV { get; set; }
    public int ThisNumber { get; set; }
}

public class Bar {
    public int BarId { get; set; }
}

public class Enquiry {
    public int EnquiryId { get; set; }
    public string Latitude { get; set; }
}

And the post action in the controller would look like this.

[ActionName("Foo"), HttpPost]
public ActionResult Foo_post(
    [Bind(Include = "EnquiryId")]
    Enquiry EnquiryV,
    [Bind(Include = "BarId"])]
    Bar BarV,
    int ThisNumber
{
    return View(new FooViewModel { Bar = BarV, Enquiry = EnquiryV, ThisNumber = ThisNumber });
}

All while the view still looks like this

@using (Html.BeginForm()) {

    @Html.TextBoxFor(m => m.EnquiryV.EnquiryId)
    @Html.TextBoxFor(m => m.EnquiryV.Latitude)
    @Html.TextBoxFor(m => m.BarV.BarId)
    @Html.TextBoxFor(m => m.ThisNumber)

    <input type="submit" value="push" />
}

Keep in mind, this form will still post Latitude back (the way you had it set up), however since it is not included in the Bind Include string for Enquiry on the post action, the action will not accept the new value in the resultant Enquiry. I'd suggest making latitude either disabled or not a form element to prevent additional posting data.

In any other scenario you can use bind just fine, but for some reason it dislikes the dot notation for complex models.

As a side note, I wouldn't put the bind attribute on the class directly as it can cause other issues like code replication, and doesn't account for certain scenarios where you may want to have a different binding.

(I modified the variable names for some clarity. I am also aware your question is rather dated, however in searching for the answer myself this is the first SO I stumbled upon before trying my own solutions and coming to the one I posted. I hope it can help out other people seeking a solution to the same issue.)