views:

414

answers:

7

Hello,

What is the best practice for validating my model for different actions, for example, different validation rules for creating and deleting?

Thanks!

Clarification: I think this needs some clarification because the answers don't seem to be addressing the question.

For example,

Create Person Validates that has name, age and email is not in use Delete Person Validates that the Person isn't a Parent. Business logic dictates that Parents can't be eliminated

How do I have these two different validation scenarios?

A: 

It's probably best to use the Validator Toolkit for ASP.NET MVC

This toolkit generates client side and server side validation code.

Here is a recent blog post that compares different validation techniques and frameworks.

If you're using jQuery then the jQuery validation plugin is worth using. Of course you still have to write your own server-side validation (or use another framework for it).

Daniel Robinson
+1  A: 

Use a validation library like FluentValidation or NHibernate Validators to do the validation. Move this code into a model binder so that whenever a model is passed it does validation automatically. You can look at the following model binder which works with FluentValidation

public class FluentValidationModelBinder : DefaultModelBinder {
        private readonly IValidatorFactory _validatorFactory;

        public FluentValidationModelBinder(IValidatorFactory validatorFactory) {
            _validatorFactory = validatorFactory;
        }

        protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext) {
            var model = bindingContext.Model;
            base.OnModelUpdated(controllerContext, bindingContext);
            IValidator validator = _validatorFactory.GetValidator(bindingContext.ModelType);
            if (validator != null) {
                var result = validator.Validate(model);
                if (!result.IsValid) {
                    result.AddToModelState(bindingContext.ModelState, "");
                }
            }
        }
    }
Khaja Minhajuddin
A: 

If you use ASP.NET MVC 1,you will check DataAnnotationsModelBinder extension. System.ComponentModel.DataAnnotations is very useful validation attribute framework! And ASP.NET MVC 2 DefaultModebinder supported it.

http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=24471

takepara
A: 

If you want to use one of the several validation frameworks for ASP.NET MVC (xVal, Data Annotations attributes, etc) and only validate a subset of a model's fields for a particular action, the easiest solution is to use the [Bind] attribute.

While I can certainly think of a case where you might only want to validate against a subset of a model's fields (i.e. in a Create action), I cannot think of a case where you would want the validations for a particular field to be completely different between two different actions.

Here is a simple example, using a Person model object in conjunction with Data Annotations validator attributes. The Create and Update action will be validating against a different set of the model's fields.


Model:

public class Person
{
  [Required]
  Int32 Id { get; set; }

  [Required]
  String FirstName {get; set;}

  [Required]
  String LastName {get; set;}
}


Controller Actions:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create( [Bind(Exclude = "Id")] Person person )
{
  // this action will not perform any validation checks against the Id property of the model
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Update( Person person )
{
  // this action will perform validation checks against all model properties, including Id
}

In the above example, the Create action will completely ignore the Id attribute of the Person model object. It will not attempt to bind this property, nor will it attempt to perform any validations against it. Since a Person would not have an Id at the time of creation, this is exactly what you want. The Update action, on the other hand, will bind and validate against all properties of the model.

The fact that the Bind attribute is specified on the method argument can make the code a bit on the ugly side. However, if the Exclude list gets long, you can always add some additional line breaks and whitespace to help reduce the ugliness.

Justin Holzer
+1  A: 

Clarification: I think this needs some clarification because the answers don't seem to be addressing the question.

For example,

Create Person Validates that has name, age and email is not in use Delete Person Validates that the Person isn't a Parent. Business logic dictates that Parents can't be eliminated

How do I have these two different validation scenarios?

"Encapsulate what varies."

One way to accomplish this is using the Command pattern, and applying your validation to the Command instead of the Entity. Here's a simple example without using any validation framework, but the idea is the same.

public class Blub
{
    public int BlubId { get; set; }
    public string Name { get; set; }
    public bool SomeBlockingCondition { get; set; }
}

public class BlubEditController : Controller
{
    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult Rename(int blubId)
    {
        var ctx = new DataContext();
        var blub = ctx.Blubs.Single(o => o.BlubId==blubId);
        return ShowRenameForm(blub);
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Rename(int blubId, RenameCommand cmd)
    {
        var ctx = new DataContext();
        var blub = ctx.Blubs.Single(o => o.BlubId==blubId);

        cmd.Subject = blub;

        if (cmd.Validate(ModelState, "cmd."))
        {
            cmd.Execute();
            ctx.SubmitChanges();
            return RedirectToAction("Show", new { blubId = blubId });
        }
        else
        {
            return ShowRenameForm(blub);
        }
    }
}

public class RenameCommand
{
    public Blub Subject { get; set; }

    public string Name { get; set; }

    public bool Validate(ModelStateDictionary modelState, string prefix)
    {
        if (Subject.SomeBlockingCondition)
        {
            modelState.AddModelError(prefix + "SomeBlockingCondition", "No!");
            return false; // Optional shortcut return to prevent further validation.
        }

        if (String.IsNullOrEmpty(this.Name))
        {
            modelState.AddModelError(prefix + "Name", "Seriously, no.");
        }

        return modelState.IsValid;
    }

    public void Execute()
    {
        Subject.Name = this.Name;
    }
}
daveidmx
Great answer. Thanks
+2  A: 

As i understand you want more control on your validation rules. You can manually add validation rules as described here: MVC 2.0 Client validation exposed This gives you full control on how to organize you validation, which rule add on which action, etc.

Hennadiy Kurabko
A: 

I think, for this case, just write some code in the domain model (BLL) of DELETE to judge if some Person can be deleted, or write some code in the sql or sp and return a value to dedicate if the delete is done.

C.T.