I make a Booking form for restaurant, which asks for the name of the restaurant, the date of the meal and the number of person.
I have a booking class, which has an ID, an ID of the restaurant, a date and a number of people :
public class Booking
{
public int Id { get; set; }
public int IDRestaurant{ get; set; }
[CustomPlaceValidator]
public int Nbpeople { get; set; }
[CustomDateValidator]
public DateTime Date { get; set; }
}
As well as a Resto class, which has an ID, a name, phone number and a number of table :
public class Resto
{
public int Id { get; set; }
[Required(ErrorMessage = "Le nom du restaurant doit être saisi")]
public string Nom { get; set; }
[Display(Name = "Téléphone")]
[RegularExpression(#"^0[0-9]{9}$", ErrorMessage = "Le numéro de téléphone est incorrect")]
public string Telephone { get; set; }
[Range(0, 9999)]
public int Size { get; set; }
}
I would like to make a validation to check with each new reservation, that the restaurant is not full.
To do this, when validating the "Number of persons" field of the Booking, I need the value of the "restaurant name" field and the value of the "date" field, and then retrieve all the bookings on this Restaurant at that date, and check whether the sum of the number of persons is much lower than the capacity of the restaurant.
public class CustomPlaceValidator : ValidationAttribute
{
private IDal dal = new Dal();
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
int nb = 0;
if (dal.GetAllBooking() != null)
{
foreach (var booking in dal.GetAllBooking())
nb += booking.Nbpeople;
if (nb ..... ) return ValidationResult.Success;
return new ValidationResult("The restaurant is full for this date.");
}
return ValidationResult.Success;
}
}
(It's a draft, the tests are not finished obviously)
How can I have the value of the other proprieties for my validation ?
This is not appropriate for a validation attribute. First, a validation attribute should be independent, or at least self-contained. Since the logic here depends on two different properties (the number of people and the date of the booking) a validation attribute would require too much knowledge of the domain in order to perform the necessary validation. In other words, it's not reusable, and if it's not reusable, then there's no point in using an attribute.
Second, a validation attribute should not do something like make a database query. The controller alone should be responsible for working with your DAL. When you start littering database access across your application, you're going to start running into all sorts of issues in very short order. If you use a DI container to inject your DAL where it needs to go, it's less problematic to use it outside of the controller, but importantly, attributes really don't play well with dependency injection. You can make it work with some DI containers, but it's never easy and you're probably going to regret it later. So, again, this really shouldn't be something a validation attribute handles.
The best approach in my opinion is to simply create a private/protected method on your controller to handle this validation. Something like:
public void ValidateCapacity(Booking booking)
{
var restaurant = dal.GetRestaurant(booking.IDRestaurant);
var existingBookings = dal.GetBookings(booking.IDRestaurant, booking.Date);
var available = restaurant.Size - existingBookings.Sum(b => b.Nbpeople);
if (booking.Nbpeople > available)
{
ModelState.AddModelError("Nbpeople", "There is not enough capacity at the restaurant for this many people on the date you've selected");
}
}
Then, in your post action for the booking, simply call this before checking ModelState.IsValid.
I'm looking at this question: Group validation messages for multiple properties together into one message asp.net mvc
My guess is something like:
public class Booking
{
public int Id { get; set; }
public int IDRestaurant{ get; set; }
[CustomPlace("IDRestaurant", "Date", ErrorMessage = "the restaurant is full")]
public int Nbpeople { get; set; }
[CustomDateValidator]
public DateTime Date { get; set; }
}
and the custom validation:
public class CustomPlaceAttribute : ValidationAttribute
{
private readonly string[] _others
public CustomPlaceAttribute(params string[] others)
{
_others= others;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
// TODO: validate the length of _others to ensure you have all required inputs
var property = validationContext.ObjectType.GetProperty(_others[0]);
if (property == null)
{
return new ValidationResult(
string.Format("Unknown property: {0}", _others[0])
);
}
// This is to get one of the other value information.
var otherValue = property.GetValue(validationContext.ObjectInstance, null);
// TODO: get the other value again for the date -- and then apply your business logic of determining the capacity
}
}
However, it feels a bit messy to do a database call for the validationAttribute though
What you are asking for is cross-property validation. If you are not strongly opposed to implementing an interface on your data objects you should take a look at the following:
https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.ivalidatableobject.aspx
A simple example implementation for a small rectangle class where we want its area not to exceed 37 (whatever that unit is).
public class SmallRectangle : IValidatableObject
{
public uint Width { get; set; }
public uint Height { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var area = Width * Height;
if (area > 37)
{
yield return new ValidationResult($"The rectangle is too large.");
}
}
}
Alternatives
The the second parameter of the IsValid function in your ValidationAttribute provides you with the ValidationContext which has the property ObjectInstance which you can cast to your object type and access its other members. That, however, will make your validation attribute specific to your class. I would generally advise against that.
You could also opt to use a different validation approach altogether such as using a validation library such as FluentValidations, see:
https://github.com/JeremySkinner/FluentValidation
A different perspective
Last but not least I would like to note that usually validation should be used to validate the integrity of the data. A booking request which requests more seats than available is not invalid. It can not be granted, but it is a valid request which will, unfortunately, be answered with a negative result. To give that negative result is, in my opinion not the responsibility of the validation, but the business logic.
Related
I want to create a small application for inserting Amount Date note Account_Name into table transaction.
I don't want to create an action method for this, and want to use from CLI.
My business object looks like this:
public class ExpenseBO : Controller
{
public void MakeExpense(MakeExpensePayload payload)
{
var transactionAccess = new TransactionAccessController(connection);
transactionAccess.MakeTransaction(payload);
}
}
In access layer:
public void MakeTransaction(MakeExpensePayload p)
{
connection.Insert(new { p.Amount, p.Date, p.Note });
}
Model MakeExpensePayload:
public class MakeExpensePayload
{
public int Amount { get; set; }
public string note { get; set; }
public DateTime Date { get; set; }
}
I want to validate Amount, Date, note, AccountName - so for instance, Amount cannot be negative, note should not be empty (i.e., make note field required). The Date is not mandatory to provide
As I am not using action methods here, I cannot validate using model validation and data annotations.
So, where should I add validations in all these structures and how can I validate these?
If you want server side validations, I consider you can do it individually. For example:
You can create a private method where you can do validations and throw an Exception to client side to inform that this field is required :
private void GeneralValidations(MakeExpensePayload payload)
{
if(payload.Amount <= 0)
//Throw new HttpException
if(string.IsNullorEmpty(payload.Note))
//Throw new HttpException
}
then, call it into your method:
public void MakeExpense(MakeExpensePayload payload)
{
GeneralValidations(payload);
var transactionAccess = new TransactionAccessController(connection);
transactionAccess.MakeTransaction(payload);
}
The rule of thumb is you should validate your data Once you receive it and before you start processing it, this will help neutralizing any possible threats
Client side validation is not enough as it can be bypassed, you should do validation also on server side
You can add Add validation conditions When you create model and use ModelState.IsValid to validate all models at once in service.
Model
public class MakeExpensePayload
{
[Range(0, int.MaxValue)]
[Required(ErrorMessage = "Amount cannot be negative")]
public int Amount { get; set; }
[Required]
public string note { get; set; }
public DateTime? Date { get; set; }
}
service
private void GeneralValidations(MakeExpensePayload payload)
{
if (ModelState.IsValid)
{
//do your logic
}
return View(model);
}
Then you can see If any properties in model validation fails, ModelState.IsValid will return false.
I am working on WinForms with EF 6.2.
I am trying to implement custom validation logic for my entities with Entity Framework.
At first, I succeeded to override the DbEntityValidationResult ValidateEntity method in my DbContext and it's working fine.
But now I have a lot of entities, it becomes very messy and I would like to implement the custom validation directly in my entities classes.
So I tried to implement the IValidatableObject interface.
Here is a simple example of an entity :
public class Inspection : IValidatableObject
{
public int Id { get; set; }
[Required(AllowEmptyStrings = false, ErrorMessage = "You must enter a description")]
[StringLength(maximumLength: 15, ErrorMessage = "The description cannot exceed 15 characters")]
public string Description { get; set; }
public DateTime? ActualDate { get; set; }
public DateTime? ValidityDate { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (ActualDate > ValidityDate)
{
ValidationResult result = new ValidationResult("Actual Date connot be > to ValidityDate");
yield return result;
}
}
}
Now I read a lot of things but cannot figure where do I have to call the Validate method of my entities, and what value I have to pass in ValidationContext parameter.
Every tutorial I've seen targets MVC scenarios so I wonder if it is possible to use it with Winforms.
I maybe have missed something, or maybe it is not the correct approach for validation in Winforms/EF.
Please can you give me some piece of advice ?
Finally I found the solution (and my error) thanks to the MSDN article :
http://msdn.microsoft.com/en-us/data/gg193959.aspx
When I read it the first time, I missed a part while implementing the interface.
I forgot to define the memberNames parameter of the ValidationResult class.
So I just changed :
ValidationResult result = new ValidationResult("Actual Date connot be > to ValidityDate");
to
ValidationResult result = new ValidationResult("Actual Date connot be > to ValidityDate",
new[] { nameof(ActualDate), nameof(ValidityDate) });
And it worked as expected.
I am still wondering what value to pass in ValidationContext parameter if the Validate method of the interface is called manually, but it is another question.
So to answer my own question : Yes, it is possible to use IValidatableObject with Winforms.
In MVC when we post a model to an action we do the following in order to validate the model against the data annotation of that model:
if (ModelState.IsValid)
If we mark a property as [Required], the ModelState.IsValid will validate that property if contains a value or not.
My question: How can I manually build and run custom validator?
P.S. I am talking about backend validator only.
In .NET Core, you can simply create a class that inherits from ValidationAttribute. You can see the full details in the ASP.NET Core MVC Docs.
Here's the example taken straight from the docs:
public class ClassicMovieAttribute : ValidationAttribute
{
private int _year;
public ClassicMovieAttribute(int Year)
{
_year = Year;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
Movie movie = (Movie)validationContext.ObjectInstance;
if (movie.Genre == Genre.Classic && movie.ReleaseDate.Year > _year)
{
return new ValidationResult(GetErrorMessage());
}
return ValidationResult.Success;
}
}
I've adapted the example to exclude client-side validation, as requested in your question.
In order to use this new attribute (again, taken from the docs), you need to add it to the relevant field:
[ClassicMovie(1960)]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
Here's another, simpler example for ensuring that a value is true:
public class EnforceTrueAttribute : ValidationAttribute
{
public EnforceTrueAttribute()
: base("The {0} field must be true.") { }
public override bool IsValid(object value) =>
value is bool valueAsBool && valueAsBool;
}
This is applied in the same way:
[EnforceTrue]
public bool ThisShouldBeTrue { get; set; }
Edit: Front-End Code as requested:
<div asp-validation-summary="All" class="text-danger"></div>
The options are All, ModelOnly or None.
To create a custom validation attribute in .Net Core, you need to inherit from IModelValidator and implement Validate method.
Custom validator
public class ValidUrlAttribute : Attribute, IModelValidator
{
public string ErrorMessage { get; set; }
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
{
var url = context.Model as string;
if (url != null && Uri.IsWellFormedUriString(url, UriKind.Absolute))
{
return Enumerable.Empty<ModelValidationResult>();
}
return new List<ModelValidationResult>
{
new ModelValidationResult(context.ModelMetadata.PropertyName, ErrorMessage)
};
}
}
The model
public class Product
{
public int ProductId { get; set; }
[Required]
public string ProductName { get; set; }
[Required]
[ValidUrl]
public string ProductThumbnailUrl { get; set; }
}
Will this approach give opportunity to work with "ModelState.IsValid" property in controller action method?
Yes! The ModelState object will correctly reflect the errors.
Can this approach be applied to the model class? Or it can be used with model class properties only?
I don't know if that could be applied onto class level. I know you can get the information about the class from ModelValidationContext though:
context.Model: returns the property value that is to be validated
context.Container: returns the object that contains the property
context.ActionContext: provides context data and describes the action method that processes the request
context.ModelMetadata: describes the model class that is being validated in detail
Notes:
This validation attribute doesn't work with Client Validation, as requested in OP.
I need to set RegularExpression Dynamically in Model.
I mean, I have stored RegularExpression in table and then I will store that value in one variable.
Now I want to supply that variable in Regular Express validation.
i.e
[RegularExpression(VariableValue, ErrorMessage = "Valid Phone is required")]
Something like
i.e
string CustomExpress = "#"^(\+|\d)(?:\+?1[-. ]?)?\(?([0-9]{2})\)?[-. ]?([0-9]{1})[-. ]?([0-9]{9})$" (from Database's table)
[RegularExpression(CustomExpress, ErrorMessage = "Valid Phone is required")]
public string Phone { get; set; }
You have two options, either you create your own validation attribute or you make your whole model "validatable".
Option 1
public class RegexFromDbValidatorAttribute : ValidationAttribute
{
private readonly IRepository _db;
//parameterless ctor that initializes _db
public override ValidationResult IsValid(object value, ValidationContext context)
{
string regexFromDb = _db.GetRegex();
Regex r = new Regex(regexFromDb);
if (value is string && r.IsMatch(value as string)){
return ValidationResult.Success;
}else{
return new ValidationResult(FormatMessage(context.DisplayName));
}
}
}
Then on your model:
[RegexFromDbValidator]
public string Telephone {get; set;}
Option 2
public SomeModel : IValidatableObject
{
private readonly IRepository _db;
//don't forget to initialize _db in ctor
public string Telephone {get; set;}
public IEnumerable<ValidationResult> Validate(ValidationContext context)
{
string regexFromDb = _db.GetRegex();
Regex r = new Regex(regexFromDb);
if (!r.IsMatch(Telephone))
yield return new ValidationResult("Invalid telephone number", new []{"Telephone"});
}
}
Here's a good resource that explains how to create validation attributes
Here's an example of using IValidatableObject
As Murali stated Data Annotation attributes values must be compile time constants.
If you want to perform dynamic validation based on other model values you can try with some kind of third party framework (e.g. Fluent Validation, it even can be integrated in ASP.NET's model validation too).
I believe there might be a way to implement this by inheriting the IValidatableObject interface. Upon doing so whenever the ModelState gets validated on the server side you could perform all the necessary checks that you wish.
It would look something like:
public class SomeClass: IValidatableObject {
private RegEx validation;
public SomeClass(RegEx val) {
this.validation = val;
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
// perform validation logic - check regex etc...
// if an error occurs:
results.Add(new ValidationResult('error message'));
}
}
How to use data annotations to do a conditional validation on model?
For example, lets say we have the following model (Person and Senior):
public class Person
{
[Required(ErrorMessage = "*")]
public string Name
{
get;
set;
}
public bool IsSenior
{
get;
set;
}
public Senior Senior
{
get;
set;
}
}
public class Senior
{
[Required(ErrorMessage = "*")]//this should be conditional validation, based on the "IsSenior" value
public string Description
{
get;
set;
}
}
And the following view:
<%= Html.EditorFor(m => m.Name)%>
<%= Html.ValidationMessageFor(m => m.Name)%>
<%= Html.CheckBoxFor(m => m.IsSenior)%>
<%= Html.ValidationMessageFor(m => m.IsSenior)%>
<%= Html.CheckBoxFor(m => m.Senior.Description)%>
<%= Html.ValidationMessageFor(m => m.Senior.Description)%>
I would like to be the "Senior.Description" property conditional required field based on the selection of the "IsSenior" propery (true -> required). How to implement conditional validation in ASP.NET MVC 2 with data annotations?
There's a much better way to add conditional validation rules in MVC3; have your model inherit IValidatableObject and implement the Validate method:
public class Person : IValidatableObject
{
public string Name { get; set; }
public bool IsSenior { get; set; }
public Senior Senior { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (IsSenior && string.IsNullOrEmpty(Senior.Description))
yield return new ValidationResult("Description must be supplied.");
}
}
Read more at Introducing ASP.NET MVC 3 (Preview 1).
I have solved this by handling the "ModelState" dictionary, which is contained by the controller. The ModelState dictionary includes all the members that have to be validated.
Here is the solution:
If you need to implement a conditional validation based on some field (e.g. if A=true, then B is required), while maintaining property level error messaging (this is not true for the custom validators that are on object level) you can achieve this by handling "ModelState", by simply removing unwanted validations from it.
...In some class...
public bool PropertyThatRequiredAnotherFieldToBeFilled
{
get;
set;
}
[Required(ErrorMessage = "*")]
public string DepentedProperty
{
get;
set;
}
...class continues...
...In some controller action ...
if (!PropertyThatRequiredAnotherFieldToBeFilled)
{
this.ModelState.Remove("DepentedProperty");
}
...
With this we achieve conditional validation, while leaving everything else the same.
UPDATE:
This is my final implementation: I have used an interface on the model and the action attribute that validates the model which implements the said interface. Interface prescribes the Validate(ModelStateDictionary modelState) method. The attribute on action just calls the Validate(modelState) on IValidatorSomething.
I did not want to complicate this answer, so I did not mention the final implementation details (which, at the end, matter in production code).
I had the same problem yesterday but I did it in a very clean way which works for both client side and server side validation.
Condition: Based on the value of other property in the model, you want to make another property required. Here is the code
public class RequiredIfAttribute : RequiredAttribute
{
private String PropertyName { get; set; }
private Object DesiredValue { get; set; }
public RequiredIfAttribute(String propertyName, Object desiredvalue)
{
PropertyName = propertyName;
DesiredValue = desiredvalue;
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
Object instance = context.ObjectInstance;
Type type = instance.GetType();
Object proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
if (proprtyvalue.ToString() == DesiredValue.ToString())
{
ValidationResult result = base.IsValid(value, context);
return result;
}
return ValidationResult.Success;
}
}
Here PropertyName is the property on which you want to make your condition
DesiredValue is the particular value of the PropertyName (property) for which your other property has to be validated for required
Say you have the following
public class User
{
public UserType UserType { get; set; }
[RequiredIf("UserType", UserType.Admin, ErrorMessageResourceName = "PasswordRequired", ErrorMessageResourceType = typeof(ResourceString))]
public string Password
{
get;
set;
}
}
At last but not the least , register adapter for your attribute so that it can do client side validation (I put it in global.asax, Application_Start)
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute),typeof(RequiredAttributeAdapter));
I've been using this amazing nuget that does dynamic annotations ExpressiveAnnotations
You could validate any logic you can dream of:
public string Email { get; set; }
public string Phone { get; set; }
[RequiredIf("Email != null")]
[RequiredIf("Phone != null")]
[AssertThat("AgreeToContact == true")]
public bool? AgreeToContact { get; set; }
You can disable validators conditionally by removing errors from ModelState:
ModelState["DependentProperty"].Errors.Clear();
Thanks Merritt :)
I've just updated this to MVC 3 in case anyone finds it useful: Conditional Validation in ASP.NET MVC 3.
There is now a framework that does this conditional validation (among other handy data annotation validations) out of the box:
http://foolproof.codeplex.com/
Specifically, take a look at the [RequiredIfTrue("IsSenior")] validator. You put that directly on the property you want to validate, so you get the desired behavior of the validation error being associated to the "Senior" property.
It is available as a NuGet package.
You need to validate at Person level, not on Senior level, or Senior must have a reference to its parent Person. It seems to me that you need a self validation mechanism that defines the validation on the Person and not on one of its properties. I'm not sure, but I don't think DataAnnotations supports this out of the box. What you can do create your own Attribute that derives from ValidationAttribute that can be decorated on class level and next create a custom validator that also allows those class-level validators to run.
I know Validation Application Block supports self-validation out-of the box, but VAB has a pretty steep learning curve. Nevertheless, here's an example using VAB:
[HasSelfValidation]
public class Person
{
public string Name { get; set; }
public bool IsSenior { get; set; }
public Senior Senior { get; set; }
[SelfValidation]
public void ValidateRange(ValidationResults results)
{
if (this.IsSenior && this.Senior != null &&
string.IsNullOrEmpty(this.Senior.Description))
{
results.AddResult(new ValidationResult(
"A senior description is required",
this, "", "", null));
}
}
}
I had the same problem, needed a modification of [Required] attribute - make field required in dependence of http request.The solution was similar to Dan Hunex answer, but his solution didn't work correctly (see comments). I don't use unobtrusive validation, just MicrosoftMvcValidation.js out of the box.
Here it is. Implement your custom attribute:
public class RequiredIfAttribute : RequiredAttribute
{
public RequiredIfAttribute(/*You can put here pararmeters if You need, as seen in other answers of this topic*/)
{
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
//You can put your logic here
return ValidationResult.Success;//I don't need its server-side so it always valid on server but you can do what you need
}
}
Then you need to implement your custom provider to use it as an adapter in your global.asax
public class RequreIfValidator : DataAnnotationsModelValidator <RequiredIfAttribute>
{
ControllerContext ccontext;
public RequreIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute)
: base(metadata, context, attribute)
{
ccontext = context;// I need only http request
}
//override it for custom client-side validation
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
//here you can customize it as you want
ModelClientValidationRule rule = new ModelClientValidationRule()
{
ErrorMessage = ErrorMessage,
//and here is what i need on client side - if you want to make field required on client side just make ValidationType "required"
ValidationType =(ccontext.HttpContext.Request["extOperation"] == "2") ? "required" : "none";
};
return new ModelClientValidationRule[] { rule };
}
}
And modify your global.asax with a line
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute), typeof(RequreIfValidator));
and here it is
[RequiredIf]
public string NomenclatureId { get; set; }
The main advantage for me is that I don't have to code custom client validator as in case of unobtrusive validation. it works just as [Required], but only in cases that you want.
Check out Simon Ince's Conditional Validation in MVC.
I am working through his example project right now.
Typical usage for conditional removal of error from Model State:
Make conditional first part of controller action
Perform logic to remove error from ModelState
Do the rest of the existing logic (typically Model State validation, then everything else)
Example:
public ActionResult MyAction(MyViewModel vm)
{
// perform conditional test
// if true, then remove from ModelState (e.g. ModelState.Remove("MyKey")
// Do typical model state validation, inside following if:
// if (!ModelState.IsValid)
// Do rest of logic (e.g. fetching, saving
In your example, keep everything as is and add the logic suggested to your Controller's Action. I'm assuming your ViewModel passed to the controller action has the Person and Senior Person objects with data populated in them from the UI.
I'm using MVC 5 but you could try something like this:
public DateTime JobStart { get; set; }
[AssertThat("StartDate >= JobStart", ErrorMessage = "Time Manager may not begin before job start date")]
[DisplayName("Start Date")]
[Required]
public DateTime? StartDate { get; set; }
In your case you would say something like "IsSenior == true".
Then you just need to check the validation on your post action.