Validation of multiple viewmodels inside the same ActionResult - asp.net-mvc

I am new to MVC. I have an ActionResult that accepts requests from clients, and I am attempting to employ MVC validation and model-binding magic. This action does not return a view, but redirects to other actions as per certain logic.
My issue is that there are different types of requests that are associated with different models, and these models are mutually exclusive.
example:
class Model1
{
[Required]
public string PropA;
}
class Model2
{
[Required]
public string PropB;
}
The logic inside the ActionResult would follow something along the likes of:
If Model1 is valid then RedirectToAction("A")
Elseif Model2 is valid then RedirectToAction("B")
Else RedirectToAction("ERROR")
What is the best practice for implementing this type of functionality, or something similar in ASP.NET MVC4?

You should be able to create a validationcontext and validate each viewmodel in your controller. You'd have to do the following for each viewmodel:
var validationResults = new List<ValidationResult>();
var context = new ValidationContext(myObject);
bool validationPassed = Validator.TryValidateObject(myObject, context, validationResults, true);

You could check the modelstate and see if it has and errors from inside the controller:
bool hasErrors = ViewData.ModelState.Values.Any(x => x.Errors.Count > 1);
[1] http://msdn.microsoft.com/en-us/library/dd470776(v=vs.108).aspx
[2] http://forums.asp.net/t/1480118.aspx/1

Related

Handling multiple select values in ASP.net MVC with a service layer

I have many multiple select dropdowns in my asp.net mvc application and am wondering what is the best way to handle transferring those values from my viewmodel to my model. The model is then saved to the database using ef6 (I'm using db first by the way to create my entities).
Currently one of my viewmodels looks something like this:
public class someviewmodel{
public string[] selectedIds
public IEnumerable<SelectListItem> SelectValues
//other properties
}
I decided to create a partial model class and add the selectedIds to it like:
public partial class somemodel{
public string[] selectedIds
//other properties
}
My model is then passed to my service layer:
public class someservicelayer{
public int InsertSomething(somemodel model){
//do stuff
if (model.selectedIds.Length != 0){
model.SelectValues = _context.SelectValues
.Where(b => model.selectedIds.Contains(b.Id.ToString())).ToList();
}
}
}
It feels wrong that I have to add a custom property in a partial class to my model. Is there a better way of handling this?

Validate an Object in ASP.NET MVC without passing it into an Action

In ASP.NET MVC you can validate the model passed to an Action with ModelState.IsValid().
I'd like to validate arbitrary objects rather than the one Model passed in. How can I do that, using the framework's libraries?
public ActionResult IsValidSoFar()
{
// Get a user's autosaved data
var json = await ...
HomeModel model = JsonConvert.Deserialize<HomeModel>(json);
// Validate the model <---- How?
}
public class HomeModel
{
[Required, MaxLength(100)]
public string Name { get; set; }
}
you can use ValidationContext class ... like below
var context = new ValidationContext(modelObject);
var results = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(modelObject, context, results);
if (!isValid)
{
foreach (var validationResult in results)
{
//validation errors
}
}
You can use ValidateModel or TryValidateModel controller methods.
ValidateModel - throws exception if model is not valid.
TryValidateModel - returns bool which indicates if model is valid.
IMPORTANT: If you validate list of models one by one, you probably would like to reset ModelState for each iteration by calling ModelState.Clear();
Please see my question regarding this: Validate list of models programmatically in ASP.NET MVC

ASP.NET MVC ViewModel with SelectList(s) best practice

I noticed that in the NerdDinner application that if the ModelState is invalid for a dinner, it merely returns the view for the model:
if (ModelState.IsValid) {
...
return RedirectToAction("Details", new { id=dinner.DinnerID });
}
return View(dinner);
However, in my application the model (a view model in this situation) contains multiple SelectLists. These lists are not instantiated at this point because this view model was just populated from the form submission. What is the recommended way to repopulate this SelectLists before sending them back to the user?
This is what I want my controller to do:
public ActionResult Save(MyModel model)
{
if (ModelState.IsValid)
{
businessClass.Save(model);
return RedirectToAction("Index", "Home");
}
// This won't work because model has uninstantiated SelectLists
return View("MyView", model);
}
I don't want to send the model to my business logic if the ModelState is invalid, but it doesn't seem to make sense to put SelectList population code in my controller. Should I create a public method in my business logic solely for doing this kind of stuff on my view model(s)?
Personally I like to keep it simple:-
[HttpGet]
public Edit(int id) {
EditForm form = new EditForm();
// Populate from the db or whatever...
PopulateEditPageSelectLists(form);
return View(form);
}
[HttpPost]
public Edit(EditForm form) {
if (ModelState.IsValid) {
// Do stuff and redirect...
}
PopulateEditPageSelectLists(form);
return View(form);
}
public void PopulateEditPageSelectLists(form) {
// Get lookup data from the db or whatever.
}
If the logic to populate the select lists is all kinds crazy it might be worthwhile moving to a separate class or whatever it but as a first step this is the best place to start.
You dont say how much reusability would you like. But personally, i like things "clear" (dont invading controller) and reausable as possible, and that in MVC means - filters.
Look at this :
public class SupplyLanguagesAttribute : System.Web.Mvc.ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext)
{
filterContext.Controller.ViewData["languagesList"] =
someService.LoadLanguagesAsDictionary();
base.OnActionExecuting(filterContext);
}
}
then you just use it with every action method where you "might" need languages :
[SupplyLanguages]
public ActionResult DoSomething()
{
...
}
And then in view, you can use the data directly for DropDownList from ViewData, or you can even "wrap" this too (and avoid "magic strings" in views), with custom reusable DropDown :
public static MvcHtmlString LanguageDropDown(this HtmlHelper html, string name, object selectValue, bool defaultOption = false)
{
var languages = html.ViewData["languagesList"] as IDictionary<string,string>;
if (languages == null || languages.Count() == 0)
throw new ArgumentNullException("LanguageDropDown cannot operate without list of languages loaded in ViewData. Use SupplyLanguages filter.");
var list = new SelectList(languages, "Key", "Value", selectValue);
return SelectExtensions.DropDownList(html, name, list);
}
My controllers populate the SelectLists on my Model if the ModelState is not valid.
Following Separation of Concerns, your business classes shouldn't know anything about the view model at all. If your view needs a list of employees your controller gets a list of employees from your business layer and creates the SelectList that your view needs.
Example
public ActionResult Save(MyModel model)
{
if (ModelState.IsValid)
{
businessClass.Save(model);
return RedirectToAction("Index", "Home");
}
model.PossibleEmployees
= _employeeRepository.All().Select(e =>
new SelectListItem{Text=e.Name,
Value=e.Id});
return View("MyView", model);
}
Update
If your select list population code is determining WHICH options to present I think you probably should move that to a service in your business layer. If reusability is the big concern, rouen's answer looks like it has the most possibility for reuse.
I use to fill lists even when the model is invalid. One other possible solution is to have an action returning the json information and build the select via ajax. SOmetimes I've also resorted to static properties / cached collections. I guess it's always depending on the particular case.
PS: You can use a local Model in each action, so I can leave initialization inside the Model constructor. (often I override a base model with [NonAction] utilities as well).
For example, I have an Employee list used widely in your application.
I've added some utility method in a base controller to build up SelectListItems and the likes. Since each and every model inherits from the base, I've got them almost everywhere in the app. Of course the Collection is filled via a dedicated business objec.
What I do is I have a static function in a class that returns a SelectList. The method accepts an Enum value which defines which SelectList to return. In the View the DropDownList or DropDownListFor functions call this function to get the SelectList.
The static function looks like this:
class HelperMethods
{
enum LookupType {Users, Companies, States};
public static SelectList CommonSelectList(LookupType type, int? filterValue = null)
//filterValue can be used if the results need to be filtered in some way
var db = new WhateverEntities();
switch (type)
{
case LookupType.Users:
var list = db.Users.OrderBy(u => u.LastName).ToList()
return new SelectList(list, "ID", "FullName")
break;
case LookupType.Companies
var list = db.Companies.OrderBy(u => u.Name).ToList()
return new SelectList(list, "ID", "Name")
break;
//and so on...
}
}
}
And the view contains this:
#Html.DropDownListFor(m => m.UserID, HelperMethods.CommonSelectList(LookupType.Users))
This way the Model and Controller does not need code to configure a SelectList to send over to the View. It makes it very easy to reuse a SelectList that has already been configured. Also, if a View needs to loop through a list of objects, then this same function can be used to get a list for that. This is the simplest and most convenient way I found of doing this.

Polymorphic model binding

This question has been asked before in earlier versions of MVC. There is also this blog entry about a way to work around the problem. I'm wondering if MVC3 has introduced anything that might help, or if there are any other options.
In a nutshell. Here's the situation. I have an abstract base model, and 2 concrete subclasses. I have a strongly typed view that renders the models with EditorForModel(). Then I have custom templates to render each concrete type.
The problem comes at post time. If I make the post action method take the base class as the parameter, then MVC can't create an abstract version of it (which i would not want anyways, i'd want it to create the actual concrete type). If I create multiple post action methods that vary only by parameter signature, then MVC complains that it's ambiguous.
So as far as I can tell, I have a few choices on how to solve this proble. I don't like any of them for various reasons, but i will list them here:
Create a custom model binder as Darin suggests in the first post I linked to.
Create a discriminator attribute as the second post I linked to suggests.
Post to different action methods based on type
???
I don't like 1, because it is basically configuration that is hidden. Some other developer working on the code may not know about it and waste a lot of time trying to figure out why things break when changes things.
I don't like 2, because it seems kind of hacky. But, i'm leaning towards this approach.
I don't like 3, because that means violating DRY.
Any other suggestions?
Edit:
I decided to go with Darin's method, but made a slight change. I added this to my abstract model:
[HiddenInput(DisplayValue = false)]
public string ConcreteModelType { get { return this.GetType().ToString(); }}
Then a hidden automatically gets generated in my DisplayForModel(). The only thing you have to remember is that if you're not using DisplayForModel(), you'll have to add it yourself.
Since I obviously opt for option 1 (:-)) let me try to elaborate it a little more so that it is less breakable and avoid hardcoding concrete instances into the model binder. The idea is to pass the concrete type into a hidden field and use reflection to instantiate the concrete type.
Suppose that you have the following view models:
public abstract class BaseViewModel
{
public int Id { get; set; }
}
public class FooViewModel : BaseViewModel
{
public string Foo { get; set; }
}
the following controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new FooViewModel { Id = 1, Foo = "foo" };
return View(model);
}
[HttpPost]
public ActionResult Index(BaseViewModel model)
{
return View(model);
}
}
the corresponding Index view:
#model BaseViewModel
#using (Html.BeginForm())
{
#Html.Hidden("ModelType", Model.GetType())
#Html.EditorForModel()
<input type="submit" value="OK" />
}
and the ~/Views/Home/EditorTemplates/FooViewModel.cshtml editor template:
#model FooViewModel
#Html.EditorFor(x => x.Id)
#Html.EditorFor(x => x.Foo)
Now we could have the following custom model binder:
public class BaseViewModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var typeValue = bindingContext.ValueProvider.GetValue("ModelType");
var type = Type.GetType(
(string)typeValue.ConvertTo(typeof(string)),
true
);
if (!typeof(BaseViewModel).IsAssignableFrom(type))
{
throw new InvalidOperationException("Bad Type");
}
var model = Activator.CreateInstance(type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
return model;
}
}
The actual type is inferred from the value of the ModelType hidden field. It is not hardcoded, meaning that you could add other child types later without having to ever touch this model binder.
This same technique could be easily be applied to collections of base view models.
I have just thought of an intersting solution to this problem. Instead of using Parameter bsed model binding like this:
[HttpPost]
public ActionResult Index(MyModel model) {...}
I can instead use TryUpdateModel() to allow me to determine what kind of model to bind to in code. For example I do something like this:
[HttpPost]
public ActionResult Index() {...}
{
MyModel model;
if (ViewData.SomeData == Something) {
model = new MyDerivedModel();
} else {
model = new MyOtherDerivedModel();
}
TryUpdateModel(model);
if (Model.IsValid) {...}
return View(model);
}
This actually works a lot better anyways, because if i'm doing any processing, then I would have to cast the model to whatever it actually is anyways, or use is to to figure out the correct Map to call with AutoMapper.
I guess those of us who haven't been using MVC since day 1 forget about UpdateModel and TryUpdateModel, but it still has its uses.
It took me a good day to come up with an answer to a closely related problem - although I'm not sure it's precisely the same issue, I'm posting it here in case others are looking for a solution to the same exact problem.
In my case, I have an abstract base-type for a number of different view-model types. So in the main view-model, I have a property of an abstract base-type:
class View
{
public AbstractBaseItemView ItemView { get; set; }
}
I have a number of sub-types of AbstractBaseItemView, many of which define their own exclusive properties.
My problem is, the model-binder does not look at the type of object attached to View.ItemView, but instead looks only at the declared property-type, which is AbstractBaseItemView - and decides to bind only the properties defined in the abstract type, ignoring properties specific to the concrete type of AbstractBaseItemView that happens to be in use.
The work-around for this isn't pretty:
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
// ...
public class ModelBinder : DefaultModelBinder
{
// ...
override protected ICustomTypeDescriptor GetTypeDescriptor(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType.IsAbstract && bindingContext.Model != null)
{
var concreteType = bindingContext.Model.GetType();
if (Nullable.GetUnderlyingType(concreteType) == null)
{
return new AssociatedMetadataTypeTypeDescriptionProvider(concreteType).GetTypeDescriptor(concreteType);
}
}
return base.GetTypeDescriptor(controllerContext, bindingContext);
}
// ...
}
Although this change feels hacky and is very "systemic", it seems to work - and does not, as far as I can figure, pose a considerable security-risk, since it does not tie into CreateModel() and thus does not allow you to post whatever and trick the model-binder into creating just any object.
It also works only when the declared property-type is an abstract type, e.g. an abstract class or an interface.
On a related note, it occurs to me that other implementations I've seen here that override CreateModel() probably will only work when you're posting entirely new objects - and will suffer from the same problem I ran into, when the declared property-type is of an abstract type. So you most likely won't be able to edit specific properties of concrete types on existing model objects, but only create new ones.
So in other words, you will probably need to integrate this work-around into your binder to also be able to properly edit objects that were added to the view-model prior to binding... Personally, I feel that's a safer approach, since I control what concrete type gets added - so the controller/action can, indirectly, specify the concrete type that may be bound, by simply populating the property with an empty instance.
Using Darin's method to discriminate your model types via a hidden field in your view, I would recommend that you use a custom RouteHandler to distinguish your model types, and direct each one to a uniquely named action on your controller. For example, if you have two concrete models, Foo and Bar, for your Create action in your controller, make a CreateFoo(Foo model) action and a CreateBar(Bar model) action. Then, make a custom RouteHandler, as follows:
public class MyRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
var httpContext = requestContext.HttpContext;
var modelType = httpContext.Request.Form["ModelType"];
var routeData = requestContext.RouteData;
if (!String.IsNullOrEmpty(modelType))
{
var action = routeData.Values["action"];
routeData.Values["action"] = action + modelType;
}
var handler = new MvcHandler(requestContext);
return handler;
}
}
Then, in Global.asax.cs, change RegisterRoutes() as follows:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
AreaRegistration.RegisterAllAreas();
routes.Add("Default", new Route("{controller}/{action}/{id}",
new RouteValueDictionary(
new { controller = "Home",
action = "Index",
id = UrlParameter.Optional }),
new MyRouteHandler()));
}
Then, when a Create request comes in, if a ModelType is defined in the returned form, the RouteHandler will append the ModelType to the action name, allowing a unique action to be defined for each concrete model.

Unit tests on MVC validation

How can I test that my controller action is putting the correct errors in the ModelState when validating an entity, when I'm using DataAnnotation validation in MVC 2 Preview 1?
Some code to illustrate. First, the action:
[HttpPost]
public ActionResult Index(BlogPost b)
{
if(ModelState.IsValid)
{
_blogService.Insert(b);
return(View("Success", b));
}
return View(b);
}
And here's a failing unit test that I think should be passing but isn't (using MbUnit & Moq):
[Test]
public void When_processing_invalid_post_HomeControllerModelState_should_have_at_least_one_error()
{
// arrange
var mockRepository = new Mock<IBlogPostSVC>();
var homeController = new HomeController(mockRepository.Object);
// act
var p = new BlogPost { Title = "test" }; // date and content should be required
homeController.Index(p);
// assert
Assert.IsTrue(!homeController.ModelState.IsValid);
}
I guess in addition to this question, should I be testing validation, and should I be testing it in this way?
Hate to necro a old post, but I thought I'd add my own thoughts (since I just had this problem and ran across this post while seeking the answer).
Don't test validation in your controller tests. Either you trust MVC's validation or write your own (i.e. don't test other's code, test your code)
If you do want to test validation is doing what you expect, test it in your model tests (I do this for a couple of my more complex regex validations).
What you really want to test here is that your controller does what you expect it to do when validation fails. That's your code, and your expectations. Testing it is easy once you realize that's all you want to test:
[test]
public void TestInvalidPostBehavior()
{
// arrange
var mockRepository = new Mock<IBlogPostSVC>();
var homeController = new HomeController(mockRepository.Object);
var p = new BlogPost();
homeController.ViewData.ModelState.AddModelError("Key", "ErrorMessage"); // Values of these two strings don't matter.
// What I'm doing is setting up the situation: my controller is receiving an invalid model.
// act
var result = (ViewResult) homeController.Index(p);
// assert
result.ForView("Index")
Assert.That(result.ViewData.Model, Is.EqualTo(p));
}
I had been having the same problem, and after reading Pauls answer and comment, I looked for a way of manually validating the view model.
I found this tutorial which explains how to manually validate a ViewModel that uses DataAnnotations. They Key code snippet is towards the end of the post.
I amended the code slightly - in the tutorial the 4th parameter of the TryValidateObject is omitted (validateAllProperties). In order to get all the annotations to Validate, this should be set to true.
Additionaly I refactored the code into a generic method, to make testing of ViewModel validation simple:
public static void ValidateViewModel<TViewModel, TController>(this TController controller, TViewModel viewModelToValidate)
where TController : ApiController
{
var validationContext = new ValidationContext(viewModelToValidate, null, null);
var validationResults = new List<ValidationResult>();
Validator.TryValidateObject(viewModelToValidate, validationContext, validationResults, true);
foreach (var validationResult in validationResults)
{
controller.ModelState.AddModelError(validationResult.MemberNames.FirstOrDefault() ?? string.Empty, validationResult.ErrorMessage);
}
}
So far this has worked really well for us.
When you call the homeController.Index method in your test, you aren't using any of the MVC framework that fires off the validation so ModelState.IsValid will always be true. In our code we call a helper Validate method directly in the controller rather than using ambient validation. I haven't had much experience with the DataAnnotations (We use NHibernate.Validators) maybe someone else can offer guidance how to call Validate from within your controller.
I'm using ModelBinders in my test cases to be able to update model.IsValid value.
var form = new FormCollection();
form.Add("Name", "0123456789012345678901234567890123456789");
var model = MvcModelBinder.BindModel<AddItemModel>(controller, form);
ViewResult result = (ViewResult)controller.Add(model);
With my MvcModelBinder.BindModel method as follows (basically the same code used
internally in the MVC framework):
public static TModel BindModel<TModel>(Controller controller, IValueProvider valueProvider) where TModel : class
{
IModelBinder binder = ModelBinders.Binders.GetBinder(typeof(TModel));
ModelBindingContext bindingContext = new ModelBindingContext()
{
FallbackToEmptyPrefix = true,
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(TModel)),
ModelName = "NotUsedButNotNull",
ModelState = controller.ModelState,
PropertyFilter = (name => { return true; }),
ValueProvider = valueProvider
};
return (TModel)binder.BindModel(controller.ControllerContext, bindingContext);
}
I was researching this today and I found this blog post by Roberto Hernández (MVP) that seems to provide the best solution to fire the validators for a controller action during unit testing. This will put the correct errors in the ModelState when validating an entity.
This doesn't exactly answer your question, because it abandons DataAnnotations, but I'll add it because it might help other people write tests for their Controllers:
You have the option of not using the validation provided by System.ComponentModel.DataAnnotations but still using the ViewData.ModelState object, by using its AddModelError method and some other validation mechanism. E.g:
public ActionResult Create(CompetitionEntry competitionEntry)
{
if (competitionEntry.Email == null)
ViewData.ModelState.AddModelError("CompetitionEntry.Email", "Please enter your e-mail");
if (ModelState.IsValid)
{
// insert code to save data here...
// ...
return Redirect("/");
}
else
{
// return with errors
var viewModel = new CompetitionEntryViewModel();
// insert code to populate viewmodel here ...
// ...
return View(viewModel);
}
}
This still lets you take advantage of the Html.ValidationMessageFor() stuff that MVC generates, without using the DataAnnotations. You have to make sure the key you use with AddModelError matches what the view is expecting for validation messages.
The controller then becomes testable because the validation is happening explicitly, rather than being done automagically by the MVC framework.
I agree that ARM has the best answer: test the behaviour of your controller, not the built-in validation.
However, you can also unit test that your Model/ViewModel has the correct validation attributes defined. Let's say your ViewModel looks like this:
public class PersonViewModel
{
[Required]
public string FirstName { get; set; }
}
This unit test will test for the existence of the [Required] attribute:
[TestMethod]
public void FirstName_should_be_required()
{
var propertyInfo = typeof(PersonViewModel).GetProperty("FirstName");
var attribute = propertyInfo.GetCustomAttributes(typeof(RequiredAttribute), false)
.FirstOrDefault();
Assert.IsNotNull(attribute);
}
In contrast to ARM, I don't have a problem with grave digging. So here is my suggestion. It builds on the answer of Giles Smith and works for ASP.NET MVC4 (I know the question is about MVC 2, but Google doesn't discriminate when looking for answers and I cannot test on MVC2.)
Instead of putting the validation code in a generic static method, I put it in a test controller. The controller has everything needed for validation. So, the test controller looks like this:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Wbe.Mvc;
protected class TestController : Controller
{
public void TestValidateModel(object Model)
{
ValidationContext validationContext = new ValidationContext(Model, null, null);
List<ValidationResult> validationResults = new List<ValidationResult>();
Validator.TryValidateObject(Model, validationContext, validationResults, true);
foreach (ValidationResult validationResult in validationResults)
{
this.ModelState.AddModelError(String.Join(", ", validationResult.MemberNames), validationResult.ErrorMessage);
}
}
}
Of course the class does not need to be a protected innerclass, that is the way I use it now but I probably am going to reuse that class. If somewhere there is a model MyModel that is decorated with nice data annotation attributes, then the test looks something like this:
[TestMethod()]
public void ValidationTest()
{
MyModel item = new MyModel();
item.Description = "This is a unit test";
item.LocationId = 1;
TestController testController = new TestController();
testController.TestValidateModel(item);
Assert.IsTrue(testController.ModelState.IsValid, "A valid model is recognized.");
}
The advantage of this setup is that I can reuse the test controller for tests of all my models and may be able to extend it to mock a bit more about the controller or use the protected methods that a controller has.
Hope it helps.
If you care about validation but you don't care about how it is implemented, if you only care about validation of your action method at the highest level of abstraction, no matter whether it is implemented as using DataAnnotations, ModelBinders or even ActionFilterAttributes, then you could use Xania.AspNet.Simulator nuget package as follows:
install-package Xania.AspNet.Simulator
--
var action = new BlogController()
.Action(c => c.Index(new BlogPost()), "POST");
var modelState = action.ValidateRequest();
modelState.IsValid.Should().BeFalse();
Based on #giles-smith 's answer and comments, for Web API:
public static void ValidateViewModel<TViewModel, TController>(this TController controller, TViewModel viewModelToValidate)
where TController : ApiController
{
var validationContext = new ValidationContext(viewModelToValidate, null, null);
var validationResults = new List<ValidationResult>();
Validator.TryValidateObject(viewModelToValidate, validationContext, validationResults, true);
foreach (var validationResult in validationResults)
{
controller.ModelState.AddModelError(validationResult.MemberNames.FirstOrDefault() ?? string.Empty, validationResult.ErrorMessage);
}
}
See on answer edit above...
#giles-smith's answer is my preferred approach but the implementation can be simplified:
public static void ValidateViewModel(this Controller controller, object viewModelToValidate)
{
var validationContext = new ValidationContext(viewModelToValidate, null, null);
var validationResults = new List<ValidationResult>();
Validator.TryValidateObject(viewModelToValidate, validationContext, validationResults, true);
foreach (var validationResult in validationResults)
{
controller.ModelState.AddModelError(validationResult.MemberNames.FirstOrDefault() ?? string.Empty, validationResult.ErrorMessage);
}
}
Instead of passing in a BlogPost you can also declare the actions parameter as FormCollection. Then you can create the BlogPost yourself and call UpdateModel(model, formCollection.ToValueProvider());.
This will trigger the validation for any field in the FormCollection.
[HttpPost]
public ActionResult Index(FormCollection form)
{
var b = new BlogPost();
TryUpdateModel(model, form.ToValueProvider());
if (ModelState.IsValid)
{
_blogService.Insert(b);
return (View("Success", b));
}
return View(b);
}
Just make sure your test adds a null value for every field in the views form that you want to leave empty.
I found that doing it this way, at the expense of a few extra lines of code, makes my unit tests resemble the way the code gets called at runtime more closely making them more valuable. Also you can test what happens when someone enters "abc" in a control bound to an int property.

Resources