On the assumption that I have Entity with couple of fields. Some fields are required at some specific state but others only on further/other state.
public class Entity
{
//Required always
public SomeReference {}
//Required in specific situation/scenario
public OtherReference {}
}
How to achieve that scenario with some known validation framework or how to do it by my self?
For help:
Udi Dahan has some thoughts on this.
http://www.udidahan.com/2007/04/30/generic-validation/
I have a solution that I am using at the moment. I use Fluent validation and am still getting used to it. I can give you an example of a simple scenario I have. maybe it helps. I have a user class, with a address Object property. At some point, I want to only validate the User details(name, email, password, etc) and at another state I want to validate the user address(first line, postcode, etc).
Classes look like this:
public class User {
public virtual string Name { get; set; }
public virtual string Email { get; set; }
public virtual string Password { get; set; }
public virtual Address Address { get; set; }
}
public class Address {
public virtual string Address1 { get; set; }
public virtual string PostCode { get; set; }
}
I then have two (simplfied) validators, one for an address and one for a user:
public AddressValidator() {
RuleFor(address => address.Address1)
.NotNull()
.WithMessage("Please enter the first line of your address");
RuleFor(address => address.PostCode)
.NotNull()
.WithMessage("Please enter your postcode")
.Matches(UK_POSTCODE_REGEX)
.WithMessage("Please enter a valid post code!");
}
public UserValidator() {
RuleFor(user => user.FirstName)
.NotNull()
.WithMessage("Please provide a first name")
.Length(3, 50)
.WithMessage("First name too short");
RuleFor(user=> user.Password)
.Length(8, 50)
.WithMessage("Password is too short");
}
I then create a Model Validator, so for example, say we have a form where the user enters an address, we create a AddressModelValidator, and can re-use the validators we have written:
public AddressModelValidator() {
RuleFor(user => user.id)
.NotNull()
.WithMessage("An error has occured, please go back and try again");
RuleFor(user => user.Address).SetValidator(new AddressValidator());
}
So, with some thought, you can really create some nice models, and reduce your validation code duplication!
My preferernce is to localize common validation functions such as email and date validations into a ValidationService class that I can pass my object into. For the rest though I tend to put the validation into the class itself. If I am using LINQ to SQL then I can create a Validate() method on my object which LINQ to SQL will call prior to saving the object to the db like this:
public void Validate()
{
if(!IsValid)
throw new ValidationException("Rule violations prevent saving");
}
public bool IsValid
{
get { return GetRuleViolations().Count() == 0;}
}
public IEnumerable<RuleViolation> GetRuleViolations()
{
if(this.TermID == 0)
yield return new RuleViolation(HelpMessageService.GetHelpMessageBodyByID(1), "agreeWithTerms");
if(ValidationService.ValidateDate(this.Birthdate.ToString()))
yield return new RuleViolation(HelpMessageService.GetHelpMessageBodyByID(2), "birthDate");
if (!(Username.Length >= ConfigurationService.GetMinimumUsernameLength()) ||
!(Username.Length <= ConfigurationService.GetMaximumUsernameLength()))
yield return new RuleViolation(HelpMessageService.GetHelpMessageBodyByID(5), "username");
if(ValidationService.ValidateUsernameComplexity(Username))
yield return new RuleViolation(HelpMessageService.GetHelpMessageBodyByID(6), "username");
if (AccountID == 0 && ObjectFactory.GetInstance<IAccountRepository>().UsernameExists(this.Username))
yield return new RuleViolation(HelpMessageService.GetHelpMessageBodyByID(7), "username");
if (!ValidationService.ValidateEmail(Email))
yield return new RuleViolation(HelpMessageService.GetHelpMessageBodyByID(8), "email");
if (AccountID == 0 && ObjectFactory.GetInstance<IAccountRepository>().EmailExists(this.Email))
yield return new RuleViolation(HelpMessageService.GetHelpMessageBodyByID(9), "email");
yield break;
}
Read here for a full understanding of this: http://nerddinnerbook.s3.amazonaws.com/Part3.htm
Related
I've made a rule that says "If Category is 'IT' or 'Part', either Username or Description must not be null or empty'.
But because I'd rather not rely on string literals for the returning the names of the required fields and strongly type them instead, I used Ilya Ivanov's answer from Using FluentValidation's WithMessage method with a list of named parameters. (I know it's not great that I have to account for the enum in a funny way.)
What I'm most interested in now is two things:
A: Should there be greater 'cohesion' between SomeUserInformationMustNotBeEmpty and CreateErrorMessage? Right now, I could accidentally validate on another property and then forget to add it to the message (or vice versa)?
B: Can the error message be dynamically generated from the validation itself (this is obviously the case for simpler rules that don't even need a WithMessage)?
So my question is: Is there any way to use a Custom Property Validator or something similar to validate and return an error message depending on what properties are passed as 'required' (i.e. !String.IsNullorEmpty(property))?
Maybe something like
RuleFor(d => d.Category)
.MustEnforceAtLeastOneNonEmpty(ListOfProperties)
.When(x => x.Category.IsIn(Category.Part, Category.IT))
which would both validate and return a message against the current rule:
"You need to enter a Username or Description when the category is IT"
And, if I added 'Age' to to the list of properties that are required, it would validate and return the message:
"You need to enter a Username, Age or Description when the category is IT"
Or maybe even:
"You need to enter a Username, Age or Description when the category is IT or Part"
My current view model:
[Validator(typeof(LeaveRequestValidator))]
public class LeaveRequestModel
{
public string Username { get; set; }
public Category Category { get; set; }
public string Description { get; set; }
public int Age { get; set; }
}
Category is an enum:
public enum Category
{
HR = 1,
Finance = 2,
Part = 3,
IT = 4
}
public class LeaveRequestValidator : AbstractValidator<LeaveRequestModel>
{
public LeaveRequestValidator()
{
// Option One. A single line and a private bool.
RuleFor(d => d)
.Must(SomeUserInformationMustNotBeEmpty)
.When(x => x.Category.IsIn(Category.Part, Category.IT))
.WithMessage("{0}", CreateErrorMessage);
private bool SomeUserInformationMustNotBeEmpty(LeaveRequestModel leaveRequestModel)
{
return (!String.IsNullOrEmpty(leaveRequestModel.Username) || !String.IsNullOrEmpty(leaveRequestModel.Description));
}
private string CreateErrorMessage(LeaveRequestModel leaveRequestModel)
{
string requiredPropertyOne = ModelMetadata.FromLambdaExpression<LeaveRequestModel, string>(x => x.Username, new ViewDataDictionary<LeaveRequestModel>()).DisplayName;
string requiredPropertyTwo = ModelMetadata.FromLambdaExpression<LeaveRequestModel, string>(x => x.Description, new ViewDataDictionary<LeaveRequestModel>()).DisplayName;
string checkedPropertyTwo = LeaveRequestModel.Category.GetType().Name.ToString();
return String.Format("You need to enter a {0} or {1} when the {2} is {3}.", requiredPropertyOne, requiredPropertyTwo, checkedPropertyTwo, LeaveRequestModel.Category);
}
}
I used a small extension to search the enums of Category.
public static class Extensions
{
public static bool IsIn<T>(this T value, params T[] list)
{
return list.Contains(value);
}
}
I have a view that is using a model and I am using that information to create a form.
I have three steps of the form that are optional or may not be shown.
The problem is that these hidden sections get posted along with the form data and break the business logic. (I have no control over the business logic)
So is there a way to tell the framework not to pass certain sections or fields? Perhaps VIA a class or something?
I know I could use AJAX to send certain sections as they are needed, but the site spec is to have them hidden and displayed as needed.
Although you could do this client-side, it won't stop malicious over-posting/mass assignment.
I suggest reading 6 Ways To Avoid Mass Assignment in ASP.NET MVC.
Excerpts:
Specify Included Properties only:
[HttpPost]
public ViewResult Edit([Bind(Include = "FirstName")] User user)
{
// ...
}
Specify Excluded Properties only:
[HttpPost]
public ViewResult Edit([Bind(Exclude = "IsAdmin")] User user)
{
// ...
}
Use TryUpdateModel()
[HttpPost]
public ViewResult Edit()
{
var user = new User();
TryUpdateModel(user, includeProperties: new[] { "FirstName" });
// ...
}
Using an Interface
public interface IUserInputModel
{
string FirstName { get; set; }
}
public class User : IUserInputModel
{
public string FirstName { get; set; }
public bool IsAdmin { get; set; }
}
[HttpPost]
public ViewResult Edit()
{
var user = new User();
TryUpdateModel<IUserInputModel>(user);
// ...
}
Use the ReadOnlyAttribute
public class User
{
public string FirstName { get; set; }
[ReadOnly(true)]
public bool IsAdmin { get; set; }
}
Lastly, and the most recommended approach is to use a real ViewModel, instead a domain Model:
public class UserInputViewModel
{
public string FirstName { get; set; }
}
Show/Hide will not allow/disallow the value from being sent to the Controller.
Elements that are Disabled or just not editable will (99% of the time) be returned as null / minVal.
You can set the elements in the View as Disabled by using JQuery in the script:
$('#elementID').attr("disabled", true);
OR you could use a DOM command:
document.getElementById('elementID').disabled = "true";
So you can set the fields as both Disabled AND Hidden, so that it is neither displayed, nor populated. Then in your Controller you can just base the Business Logic on whether or not certain fields (preferable Mandatory fields, if you have any) are null.
You can check this in C# like this:
For a string:
if (string.IsNullOrWhiteSpace(Model.stringField))
{
ModelState.AddModelError("stringField", "This is an error.");
}
For a DateTime:
if (Model.dateTimeField == DateTime.MinValue)
{
ModelState.AddModelError("dateTimeField ", "This is an error.");
}
Just for interest sake, here is how you can Hide/Show elements on the View using JQuery:
$('#elementID').hide();
$('#elementID').show();
I would like to use the built-in validation features as far as possible. I would also like to use the same model for CRUD methods.
However, as a drop down list cannot be done using the standard pattern, I have to validate it manually. In the post back method, I would like to just validate the drop down list and add this result to ModelState so that I don't have to validate all the other parameters which are done with Data Annotation. Is it possible to achieve this?
I may be mistaken about the drop down list, but from what I read, the Html object name for a drop down list cannot be the same as the property in the Model in order for the selected value to be set correctly. Is it still possible to use Data Annotation with this workaround?
Thanks.
You can use the addModelError
ModelState.AddModelError(key,message)
when you use that, it will invalidate the ModelState so isValid will return false.
Update
after seeing the comment to #Pieter's answer
If you want to exclude an element from affecting the isValid() result, you can use the ModelState.Remove(field) method before calling isValid().
Another option is to inherit IValidatableObject in your model. Implement its Validate method and you can leave all other validation in place and write whatever code you want in this method. Note: you return an empty IEnumerable<ValidationResult> to indicate there were no errors.
public class Class1 : IValidatableObject
{
public int val1 { get; set; }
public int val2 { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var errors = new List<ValidationResult>();
if (val1 < 0)
{
errors.Add(new ValidationResult("val1 can't be negative", new List<string> { "val2" }));
}
if (val2 < 0)
{
errors.Add(new ValidationResult("val2 can't be negative", new List<string> { "val2" }));
}
return errors;
}
}
EDIT: After re-reading the question I don't think this applicable to this case, but I'm leaving the answer here in case it helps someone else.
You cannot manually set the ModelState.IsValid property but you can add messages to the ModelState that will ensure that the IsValid is false.
ModelState.AddModelError();
yes, you can achieve this (also you will use the same model for CRUD methods) :
Example MODEL
public class User
{
public virtual int Id{ get; set; }
public virtual Role Role { get; set; }
}
public class Role
{
[Required(ErrorMessage = "Id Required.")]
public virtual int Id { get; set; }
[Required(ErrorMessage = "Name Required.")]
public virtual string Name { get; set; }
}
Example VIEW with validation on the dropdownlist
#Html.DropDownListFor(m => m.Role.Id, (SelectList)ViewBag.gRoles, "-- Select --")
#Html.ValidationMessageFor(m => m.Role.Id)
CONTROLLER: clearing the required (but not needed here) fields
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Creedit(User x)
{
x.Role = db.RoseSet.Find(x.Role.Id);
if (x.Role != null)
{
ModelState["Role.Name"].Errors.Clear();
}
if (ModelState.IsValid)
{
// proceed
}
else
{
// return validation error
}
}
Might be more recent methods, since this is an old post, but this might help future readers.
One can set a field to valid with this two methods:
ModelState.ClearValidationState("Password");
ModelState.MarkFieldValid("Password");
Need to use both because the second one without the first one it gives an error stating that the state is already marked.
To set a field to invalid, just use ModelState.AddModelError() method as already referred.
I have model:
[Validator(typeof(RegisterValidator))]
public class RegisterModel
{
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string ListOfCategoriess { get; set; }
}
And validator for model:
public class RegisterValidator:AbstractValidator<RegisterModel>
{
public RegisterValidator(IUserService userService)
{
RuleFor(x => x.Name).NotEmpty().WithMessage("User name is required.");
RuleFor(x => x.Email).NotEmpty().WithMessage("Email is required.");
RuleFor(x => x.Email).EmailAddress().WithMessage("Invalid email format.");
RuleFor(x => x.Password).NotEmpty().WithMessage("Password is required.");
RuleFor(x => x.ConfirmPassword).NotEmpty().WithMessage("Please confirm your password.");
}
}
I have validator factory, that should resolve dependency:
public class WindsorValidatorFactory : ValidatorFactoryBase
{
private readonly IKernel kernel;
public WindsorValidatorFactory(IKernel kernel)
{
this.kernel = kernel;
}
public override IValidator CreateInstance(Type validatorType)
{
if (validatorType == null)
throw new Exception("Validator type not found.");
return (IValidator) kernel.Resolve(validatorType);
}
}
I have IUserService, that has methods IsUsernameUnique(string name) and IsEmailUnique(string email)` and want to use it in my validator class (model should be valid only if it have unique username and email).
how to use my service for validation?
is it possible to register multiple Regular Expression Rules with different error messages? will it work on client side? (if no, how to create custom validation logic for it?)
is validation on server side will work automatically before model pass in action method, and it is enough to call ModelState.IsValid property, or I need to do something more?
UPDATE
is it possible to access to all properties of model when validate some property? (for example I want to compare Password and ConfirmPassword when register)
1) how to use my service for validation?
You could use the Must rule:
RuleFor(x => x.Email)
.NotEmpty()
.WithMessage("Email is required.")
.EmailAddress()
.WithMessage("Invalid email format.")
.Must(userService.IsEmailUnique)
.WithMessage("Email already taken");
2) is it possible to register multiple Regular Expression Rules with different error messages? will it work on client side? (if no, how to create custom validation logic for it?)
No, you can have only one validation type per property
if no, how to create custom validation logic for it?
You could use the Must rule:
RuleFor(x => x.Password)
.Must(password => SomeMethodContainingCustomLogicThatMustReturnBoolean(password))
.WithMessage("Sorry password didn't satisfy the custom logic");
3) is validation on server side will work automatically before model pass in action method, and it is enough to call ModelState.IsValid property, or I need to do something more?
Yes, absolutely. Your controller action could look like this:
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (!ModelState.IsValid)
{
// validation failed => redisplay the view so that the user
// can fix his errors
return View(model);
}
// at this stage the model is valid => process it
...
return RedirectToAction("Success");
}
UPDATE:
4) is it possible to access to all properties of model when validate some property? (for example I want to compare Password and ConfirmPassword when register)
Yes, of course:
RuleFor(x => x.ConfirmPassword)
.Equal(x => x.Password)
.WithMessage("Passwords do not match");
a nicer variant is to use a RuleBuilderExtension:
public static class RuleBuilderExtensions
{
public static IRuleBuilder<T, string> Password<T>(this IRuleBuilder<T, string> ruleBuilder, int minimumLength = 14)
{
var options = ruleBuilder
.NotEmpty().WithMessage(ErrorMessages.PasswordEmpty)
.MinimumLength(minimumLength).WithMessage(ErrorMessages.PasswordLength)
.Matches("[A-Z]").WithMessage(ErrorMessages.PasswordUppercaseLetter)
.Matches("[a-z]").WithMessage(ErrorMessages.PasswordLowercaseLetter)
.Matches("[0-9]").WithMessage(ErrorMessages.PasswordDigit)
.Matches("[^a-zA-Z0-9]").WithMessage(ErrorMessages.PasswordSpecialCharacter);
return options;
}
This way it gets trivial to use:
RuleFor(x => x.Password).Password();
I would like to use Linq and strongly typed views in the right way. at the moment I do the following:
Make a Model to verify agianst:
public class Menu
{
public int Id { get; private set; }
public string Text { get; private set; }
public string Action { get; private set; }
public string Controller { get; private set; }
public string Parameter { get; private set; }
public string Langue { get; private set; }
public Menu(int id, string controller, string action, string parameter, string text)
{
Id = id;
Controller = controller;
Action = action;
Text = text;
Parameter = parameter;
}
Use Linq to get the data from the database into the model:
public static List<Menu> GetTabListForMenu(string langue)
{
Page_dbEntities entity = new Page_dbEntities();
var tabList = (from ml in entity.wpmenulangue
where ml.Langue == langue
from m in entity.wpmenu
where ml.Menu == m.Id
from l in entity.wplangue
where ml.Langue == l.Langue
from p in entity.wppage
where p.Id == m.Page
select new { m.Id, p.Controller, p.Action, p.Parameter, ml.Text}).ToList();
List<Menu> menu = new List<Menu>();
foreach (var item in tabList)
{
menu.Add(new Menu(item.Id, item.Controller, item.Action, item.Parameter, item.Text));
}
return menu;
}
I am pretty convinced that this is not the optimal way to do this and have 2 questions:
When I get the data from the database I first use a var and then have to move it to the object with a foreach. this seems like a waste of both my time and less effeicent then getting it with sql.
I have been told that I can just verify up agianst the entitymodel. Even if i use multiple entities in a view. is this true? (the one telling me this wes not able to get it to work and I have not been able to find anything about it online).
I will try to look back on this post in the next couple of hours, but might have to wait 24 hours.
public static List<Menu> GetTabListForMenu(string langue)
{
Page_dbEntities entity = new Page_dbEntities();
return (from ml in entity.wpmenulangue
where ml.Langue == langue
from m in entity.wpmenu
where ml.Menu == m.Id
from l in entity.wplangue
where ml.Langue == l.Langue
from p in entity.wppage
where p.Id == m.Page
select new Menu(m.Id, p.Controller, p.Action, p.Parameter, ml.Text)
).ToList();
}
As for the validation is concerned you shouldn't use multiple entities in the view. You should use a single entity which is called ViewModel. This ViewModel is a class that represents the data on the view. If you are using DataAnnotations for validation you could decorate this view model properties with attributes that indicate how to be validated.