Can I do ASP.NET MVC model validation without a View? - asp.net-mvc

Can I take advantage of ASP.NET MVC model validation (using DataAnnotations for example) without an MVC View (and hence without the automatic model binding magic that you get with a view)?
As an example I might have a webhook hitting a controller action directly - no view - and I populate my model manually, but I still want to get the benefit of the DataAnnotation validations on my model's properties.

DataAnnotation is the part of System.ComponentModel.DataAnnotations.dll that comes along with .Net Framework.
Please feel free to use it.
The example you are giving me it looks like you want to expose Business Logic through WebApi.
Short answer is Yes, you can use.
However, on the top of it, have a look at expending your architecture by introducing WebApi, such that the code between MVC and WebApi is shared.
public class Recipe
{
[Required]
public string Name { get; set; }
}
var recipe = new Recipe();
var context = new ValidationContext(recipe, serviceProvider: null, items: null);
var results = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(recipe, context, results);
if (!isValid)
{
foreach (var validationResult in results)
{
Console.WriteLine(validationResult.ErrorMessage);
}
}
reference: http://odetocode.com/blogs/scott/archive/2011/06/29/manual-validation-with-data-annotations.aspx

Related

Manually trigger foolproof validation

I'm using Foolproof Validation so I can use [RequiredIf] attributes on my view model. The problem is that I'd like to trigger validation within my code using the same logic outside a controller.
I've tried creating my own validation context and using Validatior.TryValidateObject; however, it fails with Foolproof's custom RequiredIf validator. Is there a way to take my model and validate it other than passing it to a controller?
Am I using the wrong approach?
Here is my code:
var draftModel = _draftHelper.LoadDraft(draftId);
var validationResults = new List<ValidationResult>();
var vc = new ValidationContext(draftModel, null, null);
var isValidDraft = Validator.TryValidateObject(draftModel, vc,
validationResults, true);
And the error I get is on the TryValidateObject line
System.NotImplementedException: The method or operation is not
implemented.
I think a better approach would be to use FluentValidation, not foolproof validation. I personally think Fluent is nicer than attributes too :).
Using FluentValidation, you are also able to validate your model without the use of a Controller.
DraftVM draft = draftRepository.Get(draftId);
var DraftValidator validator = new DraftVMValidator();
ValidationResult results = validator.Validate(draft);
public class DraftVMValidator : AbstractValidator<DraftViewModel>
{
public DraftVMValidator()
{
RuleFor(vm => vm.OptionalField)
.Must(BeFilledIfNameNotEmpty)
.WithMessage("Optional Field required because you filled out Name field");
}
public bool BeFilledIfNameNotEmpty(DraftVM viewModel)
{
return !string.IsNullOrWhiteSpace(viewModel.Name);
}
}
This will NOT give you a System.NotImplemented exception.
This validator is DRY because you can also plug it into ASP.NET MVC Validation.
You can simply call the following code in Global.asax or App_Start etc. One validator for all, bind it to MVC Model Validation or use it in any normal application.
FluentValidationModelValidatorProvider.Configure(); // This will bind it for you
If you use Inversion of Control container like Ninject, FluentValidation also has a plugin to work with that. More available on their documentation in link provided above.
I have a pretty big project example in my Github if you want to see more examples of this Validator instead of FoolProof. Example Validators with ASP.NET MVC 4
You need to let the MVC framework provide your Validator instead of using the Validator.TryValidateObject as below:
var modelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => viewModelToValidate, viewModelToValidate.GetType());
var compositeValidator = ModelValidator.GetModelValidator(modelMetadata, controller.ControllerContext);
foreach (ModelValidationResult result in compositeValidator.Validate(null))
{
validationResults.Add(new ValidationResult(result.Message, new List<string> { result.MemberName }));
}

MVC 3 - Form Validation with annotations

The "remote" annotation in MVC 3 allows you to call an “action” to do property data validation for you. It’s beautiful! Or… is it really?
Problem: The "remote" annotation below (see comment) calls code in the client!! And my Role class is in the model. I liked "remote" because I don't need to write custom validators.
Should I use the Object View Model pattern and have the property “Role.Name” duplicated in there? That’d work. Then another problem: how do I really avoid the DRY principle (Don’t Repeat Yourself)? Is it efficient to have a property with annotations in the Object View, then the same property in the Model? I mean, is this too much work for separation of concerns?
I'm just trying to design this right and apply right design principles so I don't get burned when this website grows code-wise.
What's the best way to do this?
namespace StartWeb.Model.ObjectModel
{
public class Role //this class is in the Model (see namespace) and it needs to be "client agnostic”
{
//Then, this annotation is NOT client agnostic, it calls a controller:
[Remote("ValidateRoleName", "Role", AdditionalFields="InitialRoleName", ErrorMessage = "Role Name already exists")]
public string Name { get; set; }
This is the validation code in the RoleController action (in the "client"):
[HttpGet]
[OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
public JsonResult ValidateRoleName(string name, string initialRoleName)
{
bool isValid = true;
if (name != initialRoleName) isValid = !(new SecurityFacade().IsRoleNameExist(name));
return Json(isValid, JsonRequestBehavior.AllowGet);
}
After additional research, I found this open source tool that can do the heavy-lifting for you mapping entities from different layers:
Automapper tool between domain model and view models

How to mock a model in ASP.NET MVC?

I've made a custom model, and I want to mock it. I'm fairly new to MVC, and very new to unit testing. Most approaches I've seen create an interface for the class and then make a mock that implements the same interface. However I can't seem to get this to work when actually passing the interface into the View. Cue "simplified" example:
Model-
public interface IContact
{
void SendEmail(NameValueCollection httpRequestVars);
}
public abstract class Contact : IContact
{
//some shared properties...
public string Name { get; set; }
public void SendEmail(NameValueCollection httpRequestVars = null)
{
//construct email...
}
}
public class Enquiry : Contact
{
//some extra properties...
}
View-
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<project.Models.IContact>" %>
<!-- other html... -->
<td><%= Html.TextBoxFor(model => ((Enquiry)model).Name)%></td>
Controller-
[HttpPost]
public ActionResult Index(IContact enquiry)
{
if (!ModelState.IsValid)
return View(enquiry);
enquiry.SendEmail(Request.ServerVariables);
return View("Sent", enquiry);
}
Unit Testing-
[Test]
public void Index_HttpPostInvalidModel_ReturnsDefaultView()
{
Enquiry enquiry = new Enquiry();
_controller.ModelState.AddModelError("", "dummy value");
ViewResult result = (ViewResult)_controller.Index(enquiry);
Assert.IsNullOrEmpty(result.ViewName);
}
[Test]
public void Index_HttpPostValidModel_CallsSendEmail()
{
MockContact mock = new MockContact();
ViewResult result = (ViewResult)_controller.Index(mock);
Assert.IsTrue(mock.EmailSent);
}
public class MockContact : IContact
{
public bool EmailSent = false;
void SendEmail(NameValueCollection httpRequestVars)
{
EmailSent = true;
}
}
Upon a HttpPost I get a "Cannot create an instance of an interface" exception. I seems that I can't have my cake (passing a model) and eat it (pass mock for unit testing). Maybe there's a better approach to unit testing models bound to views?
thanks,
Med
I'm going to throw it out there, if you need to mock your models you're doing it wrong. Your models should be dumb property bags.
There is absolutely no reason that your model should have a SendEmail method. That is functionality that should be invoked from a controller calling to an EmailService.
Responding to your question:
After years of working with Separation of Concern (SOC) patterns like MVC, MVP, MVVM and seeing articles from people brighter than me (I wish I could find the one I'm thinking off about this but maybe I read it in a magazine). You will eventually conclude in an enterprise application you will end up with 3 distinct sets of model objects.
Previously I was a very big fan of doing Domain Driven Design (DDD) using a single set of business entities that were both plain old c# objects (POCO) and Persistent Ignorant (PI). Having domain models that are POCO/PI leaves you with a clean slate of objects where there is no code related to accessing the object storage or having other attributes that have schematic meaning for only 1 area of the code.
While this works, and can work fairly well for a period of time, there is eventually a tipping point where the complexity of expressing the relationship between View, Domain Model, and Physical Storage Model becomes too complex to express correctly with 1 set of entities.
To solve the impedance mismatches of View, Domain and Storage you really need 3 sets of models. Your ViewModels will exactly match your views binding to facilitate it to be easy to work with the UI. So this will frequently have things such as adding a List to populate drop downs with values that are valid for your edit view/action.
In the middle is the Domain Entities, these are the entities that you should validate against your business rules. So you will map to/from them on both sides to/from the view and to/from the storage layer. In these entities is where you could attach your code to do validation. I personally am not a fan of using attributes and coupling validation logic into your domain entities. It does make alot of sense to couple validation attributes into your ViewModels to take advantage of the built in MVC client side validation functionality.
For validation I would recommend using a library like FluentValidation (or your own custom one, they're not hard to write) that lets you separate your business rules from your objects. Although with new features with MVC3 you can do remote validation severside and have it display client side, this is an option to handle true business validation.
Finally you have your storage models. As I said previously I was very zealous on having PI objects being able to be reused through all layers so depending on how you setup your durable storage you might be able to directly use your domain objects. But if you take advantage of tools like Linq2Sql, EntityFramework (EF) etc you will most likely have auto generated models with code for interacting with the data provider so you will want to map your domain objects to your persistence objects.
So wrap all of this up this would be a standard logic flow in MVC actions
User goes to edit product page
EF queries the database to get the existing product information, inside the repository layer the EF data objects are mapped to the Business Entities (BE) so all the data layer methods return BEs and have no external coupling to the EF data objects. (So if you ever change your data provider you don't have to alter a single line of code except for the internal implementation)
The controller gets the Product BE and maps it to a Product ViewModel (VM) and adds collections for the different options that can be set for drop down lists
Return View(theview, ProductVM)
User edits the product and submits the form
Client side validation is passed (useful for date validation / number validation instead of having to submit the form for feedback)
The ProductVM gets mapped back to ProductBE at this point you would validate the business rules along the lines ValidationFactory.Validate(ProductBE), if it's invalid return messages back to view and cancel edit, otherwise continue
You pass the ProductBE into your repository model, inside the internal implementation of the data layer you map the ProductBE to the Product Data Entity for EF and update the database.
2016 edit: removed usages of Interface as separation of concerns and interfaces are entirely orthogonal.
Your issue is here:
public ActionResult Index(IContact enquiry)
MVC in the background has to create a concrete type to pass to the method when calling it. In this method's case, MVC needs to create a type which implements IContract.
Which type? I dunno. Neither does MVC.
Instead of using interfaces in order to be able to mock your models, use normal classes that have protected methods which you can override in mocks.
public class Contact
{
//some shared properties...
public string Name { get; set; }
public virtual void SendEmail(NameValueCollection httpRequestVars = null)
{
//construct email...
}
}
public class MockContact
{
//some shared properties...
public string Name { get; set; }
public bool EmailSent {get;private set;}
public override void SendEmail(NameValueCollection vars = null)
{
EmailSent = true;
}
}
and
public ActionResult Index(Contact enquiry)
It is possible to use interfaces.
See: http://mvcunity.codeplex.com/

Fetching data within an ASP.NET MVC ViewModel class?

For those that create ViewModels (for use by typed views) in ASP.NET MVC, do you prefer to fetch the data from a service/repository from within the ViewModel, or the controller classes?
For example, we started by having ViewModels essentially being DTOs and allowing our Controllers to fetch the data (grossly oversimplified example assumes that the user can only change employee name):
public class EmployeeViewModel
{
public String Name; //posted back
public int Num; //posted back
public IEnumerable<Dependent> Dependents; //static
public IEnumerable<Spouse> Spouses; //static
}
public class EmployeeController()
{
...
public ActionResult Employee(int empNum)
{
Models.EmployeeViewModel model = new Models.EmployeeViewModel();
model.Name = _empSvc.FetchEmployee(empNum).Name;
model.Num = empNum;
model.Dependents = _peopleSvc.FetchDependentsForView(empNum);
model.Spouses = _peopleSvc.FetchDependentsForView(empNum);
return View(model);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Employee(Models.EmployeeViewModel model)
{
if (!_empSvc.ValidateAndSaveName(model.Num, model.Name))
{
model.Dependents = _peopleSvc.FetchDependentsForView(model.Num);
model.Spouses = _peopleSvc.FetchDependentsForView(model.Num);
return View(model);
}
this.RedirectToAction(c => c.Index());
}
}
This all seemed fine until we started creating large views (40+ fields) with many drop downs and such. Since the screens would have a GET and POST action (with POST returning a view if there was a validation error), we'd be duplicating code and making ViewModels larger than they probably should be.
I'm thinking the alternative would be to Fetch the data via the Service within the ViewModel. My concern is that we'd then have some data populated from the ViewModel and some from the Controller (e.g. in the example above, Name would be populated from the Controller since it is a posted value, while Dependents and Spouses would be populated via some type of GetStaticData() function in the ViewModel).
Thoughts?
I encountered the same issue. I started creating classes for each action when the code got too big for the action methods. Yes you will have some data retrieval in classes and some in the controller methods. The alternative is to have all the data retrieval in classes, but half the classes you won't really need, they will have been created for consistency sake or have all the data retrieval in the controller methods, but again, some of those methods will be too complex and needed to have been abstracted into classes... so pick your poison. I would rather have a little inconsistency and have the right solution for the job.
As for putting behavior into the ViewModel, I don't, the point of the ViewModel is to be a thin class for setting and extracting values from the View.
There have been cases where I've put conversion methods in the ViewModel. For instance I need to convert the ViewModel to the corresponding entity or I need to load the ViewModel with data from the Entity.
To answer your question, I prefer to retrieve data from with in the controller/action methods.
Typically with DropDowns, I create a dropdown service. DropDowns tend to be the same data that spans views. With the dropdowns in a service I can use them on other views and/or Cache them.
Depending on the layout, 40 plus fields could create a cluttered view. Depending the type of data, I would try to span that many fields across multiple views with some sort of tabbed or wizard interface.
There's more than that ;-) You can fetch in model binder or action filter. For the second option, check Jimmy Bogard's blog somewhere around here. I personally do it in model binders. I use ViewModel like this: My custom ASP.NET MVC entity binding: is it a good solution?. It is processed by my custom model binder:
public object BindModel(ControllerContext c, BindingContext b)
{
var id = b.ValueProvider[b.ModelName]; // don't remember exact syntax
var repository = ServiceLocator.GetInstance(GetRepositoryType(b.ModelType));
var obj = repository.Get(id);
if (obj == null)
b.ModelState.AddModelError(b.ModelName, "Not found in database");
return obj;
}
public ActionResult Action(EntityViewModel<Order> order)
{
if (!ModelState.IsValid)
...;
}
You can also see an example of model binder doing repository access in S#arp Architecture.
As for static data in view models, I'm still exploring approaches. For example, you can have your view models remember the entities instead of lists, and
public class MyViewModel
{
public MyViewModel(Order order, IEmployeesSvc _svc)
{
}
public IList<Employee> GetEmployeesList()
{
return _svc.GetEmployeesFor(order.Number);
}
}
You decide how you inject _svc into ViewModel, but it's basically the same as you do for controller. Just beware that ViewModel is also created by MVC via parameterless constructor, so you either use ServiceLocator or extend MVC for ViewModel creation - for example, inside your custom model binder. Or you can use Jimmy Bogard's approach with AutoMapper which does also support IoC containers.
The common approach here is that whenever I see repetative code, I look to eliminate it. 100 controller actions doing domain-viewmodel marshalling plus repository lookup is a bad case. Single model binder doing it in generic way is a good one.
I wouldn't be fetching data from the database in your ViewModel. The ViewModel exists to promote separation of concerns (between your View and your Model). Tangling up persistance logic in there kind of defeats the purpose.
Luckily, the ASP.NET MVC framework gives us more integration points, specifically the ModelBinder.
I've got an implementation of a generic ModelBinder pulling information from the service layer at:-
http://www.iaingalloway.com/going-further-a-generic-servicelayer-modelbinder
It doesn't use a ViewModel, but that's easily fixed. It's by no means the only implementation. For a real-world project, you're probably better off with a less generic, more customised solution.
If you're diligent, your GET methods don't even need to know that the service layer exists.
The solution probably looks something like:-
Controller action method:-
public ActionResult Details(MyTypeIndexViewModel model)
{
if( ModelState.IsValid )
{
return View(model);
}
else
{
// Handle the case where the ModelState is invalid
// usually because they've requested MyType/Details/x
// and there's no matching MyType in the repository
// e.g. return RedirectToAction("Index")
}
}
ModelBinder:-
public object BindModel
(
ControllerContext controllerContext,
BindingContext bindingContext
)
{
// Get the Primary Key from the requestValueProvider.
// e.g. bindingContext.ValueProvider["id"]
int id = ...;
// Get an instance of your service layer via your
// favourite dependancy injection framework.
// Or grab the controller's copy e.g.
// (controllerContext.Controller as MyController).Service
IMyTypeService service = ...;
MyType myType = service.GetMyTypeById(id)
if (myType == null)
{
// handle the case where the PK has no matching MyType in the repository
// e.g. bindingContext.ModelState.AddModelError(...)
}
MyTypeIndexViewModel model = new MyTypeIndexViewModel(myType);
// If you've got more repository calls to make
// (e.g. populating extra fields on the model)
// you can do that here.
return model;
}
ViewModel:-
public class MyTypeIndexViewModel
{
public MyTypeIndexViewModel(MyType source)
{
// Bind all the properties of the ViewModel in here, or better
// inherit from e.g. MyTypeViewModel, bind all the properties
// shared between views in there and chain up base(source)
}
}
Build your service layer, and register your ModelBinder as normal.
Here's another solution: http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/06/29/how-we-do-mvc-view-models.aspx
Main points there:
Mapping is done by a mediator - in this case it is AutoMapper but it can be your own class (though more to code). This keeps both Domain and ViewModel concentrated on the domain/presentation logic. The mediator (mapper) will contain (mostly automatic) logic for mapping, including injected services.
Mapping is applied automatically, all you do is tell the action filter the source/destination types - very clean.
(Seems to be important for you) AutoMapper supports nested mappings/types, so you can have your ViewModel combined of several independent view models, so that your "screen DTO" is not messy.
Like in this model:
public class WholeViewModel
{
public Part1ViewModel ModelPart1 { get; set; }
public Part2ViewModel ModelPart2 { get; set; }
}
you re-use mappings for specific parts of your View, and you don't write any new line of code, since there're already mappings for the partial view models.
If you don't want AutoMapper, you have have IViewModelMapper interfaces, and then your IoC container will help your action filter to find appropriate
container.Resolve(typeof(IViewModelMapper<>).MakeGenericType(mysourcetype, mydesttype))
and it will also provide any required external services to that mapper (this is possible with AutoMapper, too). But of course AutoMapper can do recursions and anyway, why write additional AutoMapper ;-)
Consider passing your services into the custom ViewModel on its constructor (ala Dependency Injection). That removes the model population code from your controller and allows it to focus on controlling the logical flow of the application. Custom ViewModels are an ideal place to abstract the preparation of things like SelectLists that your droplists will depend on.
Lots of code in the controller for things like retrieving data isn't considered a best practice. The controller's primary responsibility is to "control" the flow of the application.
Submitting this one late... Bounty is almost over. But...
Another mapper to look at is Automapper: http://www.codeplex.com/AutoMapper
And overview on how to use it: http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/01/22/automapper-the-object-object-mapper.aspx
I really like it's syntax.
// place this somewhere in your globals, or base controller constructor
Mapper.CreateMap<Employee, EmployeeViewModel>();
Now, in your controller, I would use multiple viewmodels. This enforces DRY by allowing you to reuse those viewmodels elsewhere in your application. I would not bind them all to 1 viewmodel. I would refactor to something like:
public class EmployeeController()
{
private IEmployeeService _empSvc;
private ISpouseService _peopleSvc;
public EmployeeController(
IEmployeeService empSvc, ISpouseService peopleSvc)
{
// D.I. hard at work! Auto-wiring up our services. :)
_empSvc = empSvc;
_peopleSvc = peopleSvc;
// setup all ViewModels here that the controller would use
Mapper.CreateMap<Employee, EmployeeViewModel>();
Mapper.CreateMap<Spouse, SpouseViewModel>();
}
public ActionResult Employee(int empNum)
{
// really should have some validation here that reaches into the domain
//
var employeeViewModel =
Mapper.Map<Employee, EmployeeViewModel>(
_empSvc.FetchEmployee(empNum)
);
var spouseViewModel =
Mapper.Map<Spouses, SpousesViewModel>(
_peopleSvc.FetchSpouseByEmployeeID(empNum)
);
employeeViewModel.SpouseViewModel = spouseViewModel;
return View(employeeViewModel);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Employee(int id, FormCollection values)
{
try
{
// always post to an ID, which is the employeeID
var employee = _empSvc.FetchEmployee(id);
// and bind using the built-in UpdateModel helpers.
// this will throw an exception if someone is posting something
// they shouldn't be posting. :)
UpdateModel(employee);
// save employee here
this.RedirectToAction(c => c.Index());
}
catch
{
// check your domain model for any errors.
// check for any other type of exception.
// fail back to the employee screen
RedirectToAction(c => c.Employee(id));
}
}
}
I generally try to stay away from saving multiple entities on a controller action. Instead, I would refactor the employee domain object to have AddSpouse() and SaveSpouse() methods, that would take an object of Spouse. This concept is known as AggregateRoots, controlling all dependancies from the root - which is the Employee() object. But, that is just me.

Conditionally validating portions of an ASP.NET MVC Model with DataAnnotations?

I have certain panels on my page that are hidden under certain circumstances.
For instance I might have a 'billing address' and 'shipping address' and I dont want to validate 'shipping address' if a 'ShippingSameAsBilling' checkbox is checked.
I am trying to use the new DataAnnotations capabilities of ASP.NET MVC 2 (preview 1) to achieve this.
I need to prevent validation of the 'shipping address' when it is not displayed and need to find the way way to achieve this. I am talking mainly server side as opposed to by using jquery.
How can I achieve this? I have had several ideas, related to custom model binding but my current best solution is below. Any feedback on this method?
For the CheckoutModel I am using this approach (most fields hidden):
[ModelBinder(typeof(CheckoutModelBinder))]
public class CheckoutModel : ShoppingCartModel
{
public Address BillingAddress { get; set; }
public Address ShippingAddress { get; set; }
public bool ShipToBillingAddress { get; set; }
}
public class Address
{
[Required(ErrorMessage = "Email is required")]
public string Email { get; set; }
[Required(ErrorMessage = "First name is required")]
public string FirstName { get; set; }
[Required()]
public string LastName { get; set; }
[Required()]
public string Address1 { get; set; }
}
The custom model binder removes all ModelState errors for fields beginning with 'ShippingAddress' if it finds any. Then 'TryUpdateModel()' will return true.
public class CheckoutModelBinder : DefaultModelBinder
{
protected override void OnModelUpdated(ControllerContext controllerContext,
ModelBindingContext bindingContext) {
base.OnModelUpdated(controllerContext, bindingContext);
var model = (CheckoutModel)bindingContext.Model;
// if user specified Shipping and Billing are the same then
// remove all ModelState errors for ShippingAddress
if (model.ShipToBillingAddress)
{
var keys = bindingContext.ModelState.Where(x => x.Key.StartsWith("ShippingAddress")).Select(x => x.Key).ToList();
foreach (var key in keys)
{
bindingContext.ModelState.Remove(key);
}
}
}
}
Any better solutions?
http://bradwilson.typepad.com/blog/2009/04/dataannotations-and-aspnet-mvc.html
I can see your predicament. I'm looking for other validation solutions also with regard to complex validation rules that might apply to more than one property on a given model object or even many properties from different model objects in a object graph (if your unlucky enough to be validating linked objects like this).
The limitation of the IDataErrorInfo interface is that a model object satisfies the valid state simply when none of the properties have errors. This is to say that a valid object is one where all of it's properties are also valid. However, i may have a situation where if property A, B and C are valid - then the whole object is valid.. but also if property A is not valid but B and C are, then the object satisfies validity. I simply have no way of describing this condition/rule with the IDataErrorInfo interface / DataAnnotations attributes.
So i found this delegate approach. Now many of the helpful advancements in MVC didn't exist at the time of writing this article but the core concept should help you. Rather than using attributes to define the validation conditions of an object we create delegate functions that validate more complex requirements and because they're delegated we can re-use them. Sure it's more work, but the use of delegates means that we should be able to write validation rule code once and store all the validation rules in the one place (maybe service layer) and (the kool bit) even use the MVC 2 DefaultModelBinder to invoke the validation automatically (without heaps of checking in our controller actions - like Scott's blog says we can do with DataAnnotations. Refer to the last paragraph before the 'Strongly Typed UI Helpers' heading)!
I'm sure you can beef the approach suggested in the above article up a little with anonymous delegates like Func<T> or Predicate<T> and writing custom code blocks for the validation rules will enable cross-property conditions (for example the condition you referred to where if your ShippingSameAsBilling property is true then you can ignore more rules for the shipping address, etc).
DataAnnotations serves to make simple validation rules on objects really easy with very little code. But as your requirements develop you will need to validate on more complex rules. The new virtual methods in the MVC2 model binder should continue to provide us with ways of integrating our future validation inventions into the MVC framework.
Make sure the fields you don't want validated are not posted to the action. We only validate the fields that were actually posted.
Edit: (by questioner)
This behavior has changed in MVC2 RC2 :
Default validation system validates
entire model The default validation
system in ASP.NET MVC 1.0 and in
previews of ASP.NET MVC 2 prior to RC
2 validated only model properties that
were posted to the server. In ASP.NET
MVC 2, the new behavior is that all
model properties are validated when
the model is validated, regardless of
whether a new value was posted.
Applications that depend on the
ASP.NET MVC 1.0 behavior may require
changes. For more information about
this change, see the entry Input
Validation vs. Model Validation in
ASP.NET MVC on Brad Wilson’s blog.
For the more complex cases I moved away from simple DataAnnotations to the following: Validation with visitors and extension methods.
If you want to make use of your DataAnnotations you would replace something like the following:
public IEnumerable<ErrorInfo> BrokenRules (Payment payment)
{
// snip...
if (string.IsNullOrEmpty (payment.CCName))
{
yield return new ErrorInfo ("CCName", "Credit card name is required");
}
}
with a method to validate a property by name via DataAnnotations (which I don't have atm).
I created a partial model binder that only validates the keys that were submitted. For security reasons (if I was going to take this a step farther) I'd create a data annotation attribute that marks which fields are allowed to be excluded from a model. Then, OnModelUpdated check field attributes to ensure there is no undesired underposting going on.
public class PartialModelBinder : DefaultModelBinder
{
protected override void OnModelUpdated(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
// default model binding to get errors
base.OnModelUpdated(controllerContext, bindingContext);
// remove errors from filds not posted
// TODO: include request files
var postedKeys = controllerContext.HttpContext.Request.Form.AllKeys;
var unpostedKeysWithErrors = bindingContext.ModelState
.Where(i => !postedKeys.Contains(i.Key))
.Select(i=> i.Key).ToList();
foreach (var key in unpostedKeysWithErrors)
{
bindingContext.ModelState.Remove(key);
}
}
}
This isn't related to DataAnnotations but have you looked at the Fluent Validation project? It gives you fine grain control over your validation and if you have object-to-object validation an aggregate object of the two objects will get you going.
Also it seems to have been build with MVC in mind but it also has its own "runtime" so that you can use it in other .NET applications as well which is another bonus in my book.

Resources