Reputation: 53
I have a MVC web project. According to best practice, where is the correct place to add my validation rules and business rules?
Validation rules would be the required fields and required formats.
Business rules would be "this email is already taken in the database"
Here's how I am currently doing it in my register model:
public class RegisterModel : IValidatableObject
{
[Display(Name = "Email address")]
[Required(ErrorMessage = "The email address is required")]
[EmailAddress(ErrorMessage = "Invalid Email Address")]
public string Email { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var retVal = new List<ValidationResult>();
using (var entity = new AcademicUniteDatabaseEntities())
{
if (entity.UserProfiles.Any(x => x.UserName == this.Email))
{
retVal.Add(new ValidationResult("Email already exist", new List<string> { "Email" }));
}
}
return retVal;
}
}
Here's my register controller:
public ActionResult Register()
{
var model = new RegisterModel();
return this.View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
if (!this.ModelState.IsValid)
{
return this.View(model);
}
model.CreateAccount();
return this.View("WaitConfirmEmail");
}
Why I'm doing it this way
Is this best practice?
Upvotes: 3
Views: 3432
Reputation: 9281
Using annotations to validate your models is fine for small applications that require validation, but when your validation rules start to become more and more complicated and your models begin to get more complex then I'd suggest looking at a library like Fluent Validation.
The problem with validate your models via attributes is it can be quite messy and you'll begin to suffer from a lot of repetition; where as the benefits you gain from using a library like Fluent Validation are:
An example of a typical Fluent Validation rule:
public class CustomerValidator: AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(customer => customer.Surname).NotEmpty();
RuleFor(customer => customer.Forename).NotEmpty().WithMessage("Please specify a first name");
RuleFor(customer => customer.Discount).NotEqual(0).When(customer => customer.HasDiscount);
RuleFor(customer => customer.Address).Length(20, 250);
RuleFor(customer => customer.Postcode).Must(BeAValidPostcode).WithMessage("Please specify a valid postcode");
}
private bool BeAValidPostcode(string postcode)
{
// custom postcode validating logic goes here
}
}
Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator();
ValidationResult results = validator.Validate(customer);
bool validationSucceeded = results.IsValid;
IList<ValidationFailure> failures = results.Errors;
Upvotes: 3
Reputation: 11320
One alternative is to create a custom validator attribute. With your solution, you could end up with very big models. Another alternative is to create static helpers that you can plug in the controller. There is no right answer here, it's just the way you'd like to organize code. My preference is to spread the business logic across custom attributes you can plug and play. This way you can refactor seamlessly.
Upvotes: 0