Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC - How to implement an optional nested model with required fields?

I'm currently in the process of implementing a form in an ASP.NET MVC4 application and I can't seem to find a good and maintainable solution concerning input validation. Given the following (simplified) model:

public class PersonalData
{
    [Required]
    public string FirstName { get; set; }

    [Required]
    public string LastName { get; set; }

    [Required]
    public AddressData ResidentialAddress { get { return residentialAddress; } }
    private readonly AddressData residentialAddress = new AddressData();

    public AddressData PostalAddress { get; set; }
}

public class AddressData
{
    [Required]
    public string ZipCode { get; set; }

    [Required]
    public string City { get; set; }

    [Required]
    public string Street { get; set; }

    [Required]
    public int HouseNumber { get; set; }
}

I think that this model definition pretty much speaks for itself: I'm capable of declaring properties on the 'PersonalData'-class as 'Required', and I'm also capable of doing the same on the 'AddressData'-model. This approach works with the 'ResidentialAddress'-property, but it gets tricky however with optional nested models, such as the one provided in the 'PostalAddress'-property.

Simply put:

  • All string properties on the 'PersonalData'-model are required;
  • All string properties on the 'AddressData'-object stored in the 'ResidentialAddress'-property are required;
  • 'PostalAddress' is optional: All string properties on the 'AddressData'-object stored in the 'PostalAddress'-property are required if an 'AddressData'-instance was specified to it.

'Flattening out' the PersonalData-model doesn't seem like a desirable solution to me, especially considering the fact that we're using an EditorTemplate.

What are my options? Am I doing something wrong on a fundamental level? Is there a generally supported technique for this?

like image 591
Rob Wijkstra Avatar asked Nov 02 '22 07:11

Rob Wijkstra


1 Answers

I actually found a solution that suits my need! Suppose that a controller has this method:

[HttpPost]
public ActionResult Edit(PersonalData model)
{
    if (ModelState.IsValid)
    {
        // Success
        return Redirect("NextAction");
    }
    return View(model);
}

The instantiation of the 'PersonalData'-object that is used in the 'model'-parameter here is based on the deserialization of the POST-variables that were being sent.

Now, if the following POST-variables were being sent:

  • FirstName: 'John'
  • LastName: 'Doe'
  • ResidentialAddress.ZipCode: '1234AB'
  • ResidentialAddress.City: 'My City'
  • ResidentialAddress.Street: 'My Street'
  • ResidentialAddress.HouseNumber: '123'

Then it will be serialized into a 'PersonalData'-instance, on which the 'ResidentialAddress'-property must be assigned with a valid 'AddressData'-instance. (MVC seems to take care of this automatically).

The presence of 'PostalAddress'-related POST-variables, as follows...

  • PostalAddress.ZipCode: '1234AB'
  • PostalAddress.City: 'My City'
  • PostalAddress.Street: 'My Street'
  • PostalAddress.HouseNumber: '123'

...appears to control whether or not the serialization will result in the instantiation of an 'AddressData'-instance to the 'PostalAddress'-property, which exactly covers the case that I want.

Bottom line: maintain control over what POST-variables are being sent and you should be OK! I hope this helps anyone out; suggestions and/or comments are welcome!

like image 75
Rob Wijkstra Avatar answered Nov 11 '22 15:11

Rob Wijkstra