I have two ViewModels (simplified):
public class ParentViewModel
{
public ParentViewModel
{
Content = new ChildViewModel();
}
public ChildViewModel Content { get; set, }
}
public class ChildViewModel
{
[Required]
public string Name1 { get; set, }
[Required]
public string Name2 { get; set, }
}
And the following controller post action:
[HttpPost]
public ActionResult Create(ParentViewModel viewModel)
{
if (ModelState.IsValid)
{
// process viewModel -> write something into database
return RedirectToAction("Index");
}
return View(viewModel);
}
Now I am sending the following form values in a post request body to the URL corresponding to that action (manually in Fiddler Request Builder):
Content.Name1=X
This works fine, the
Name1
property is filled inviewModel.Content
,Name2
isnull
and the model state is invalid becauseName2
is required. So, validation fails as expected.Xontent.Name1=X or Name1=X or whatever so that nothing gets bound to the
viewModel
Now
viewModel.Content
is notnull
(because I'm instantiating it in the constructor) but all propertiesName1
andName2
arenull
. This is expected. What I did not expect is that the model state is valid, so it passes the validation (leading to DB exceptions later because there are non-nullable columns).
How can I improve this code so that validation also works in the second case?
I did three experiments:
I have removed the instantiation of
Content
in theParentViewModel
constructor, thenContent
isnull
in the second example above, but validation still passes.I have added a
[Required]
attribute to theContent
property (but didn't remove the instantiation ofContent
in theParentViewModel
constructor). This has no effect at all, the described behaviour of the two tests above is the same.I have added a
[Required]
attribute to theContent
property and removed the instantiation ofContent
in theParentViewModel
constructor. This seems to work as I want: In the second testContent
isnull
and validation fails due to the[Required]
attribute. It would look like this:public class ParentViewModel { [Required] public ChildViewModel Content { get; set, } } public class ChildViewModel { [Required] public string Name1 { get; set, } [Required] public string Name2 { get; set, } }
I would conclude now that instantiating the Content
child property in the ParentViewModel
constructor is the source of the problem and that the model binder itself must instantiate the child properties (or not, if there are no matching form fields in the request) in order to have a properly working server side validation.
I have child property instantiation in several other view model constructors and didn't notice this problem until now. So, is this generally a bad practice? Are there other ways to solve the problem?