What is the best way of adding "Different than zero" validation attribute in ASP.NET MVC 3/4?
I would assume you would want to add a custom validation attribute with the error message "Different than zero" (not sure of the correct English usage here). Say you have a model with a property that requires the validation:
public class YourModel
{
[Required]
[CheckZero] //custom attribute
public int PropertyName { get; set; }
...
}
Create a class CheckZeroAttribute that derives from ValidationAttribute and override one of the IsValid methods provided by the base class. Overriding the IsValid version taking a ValidationContext parameter provides more information to use inside the IsValid method (the ValidationContext parameter will give you access to the model type, model object instance, and friendly display name of the property you are validating, among other pieces of information).
public class CheckZeroAttribute : ValidationAttribute
{
protected override ValidationResult IsValid (object value, ValidationContext validationContext)
{
//the error message
string sErrorMessage = "Different from zero";
//implement appropriate validation logic here
...
if (...) { return new ValidationResult(sErrorMessage); }
return ValidationResult.Success;
}
}
Related
I have developed a custom validator Attribute class for checking Integer values in my model classes. But the problem is this class is not working. I have debugged my code but the breakpoint is not hit during debugging the code. Here is my code:
public class ValidateIntegerValueAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null)
{
int output;
var isInteger = int.TryParse(value.ToString(), out output);
if (!isInteger)
{
return new ValidationResult("Must be a Integer number");
}
}
return ValidationResult.Success;
}
}
I have also an Filter class for model validation globally in application request pipeline. Here is my code:
public class MyModelValidatorFilter: IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
if (context.ModelState.IsValid)
return;
var errors = new Dictionary<string, string[]>();
foreach (var err in actionContext.ModelState)
{
var itemErrors = new List<string>();
foreach (var error in err.Value.Errors){
itemErrors.Add(error.Exception.Message);
}
errors.Add(err.Key, itemErrors.ToArray());
}
actionContext.Result = new OkObjectResult(new MyResponse
{
Errors = errors
});
}
}
The model class with validation is below:
public class MyModelClass
{
[ValidateIntegerValue(ErrorMessage = "{0} must be a Integer Value")]
[Required(ErrorMessage = "{0} is required")]
public int Level { get; set; }
}
Can anyone please let me know why the attribute integer validation class is not working.
Model validation comes into play after the model is deserialized from the request. If the model contains integer field Level and you send value that could not be deserialized as integer (e.g. "abc"), then model will not be even deserialized. As result, validation attribute will also not be called - there is just no model for validation.
Taking this, there is no much sense in implementing such ValidateIntegerValueAttribute. Such validation is already performed by deserializer, JSON.Net in this case. You could verify this by checking model state in controller action. ModelState.IsValid will be set to false and ModelState errors bag will contain following error:
Newtonsoft.Json.JsonReaderException: Could not convert string to
integer: abc. Path 'Level', ...
One more thing to add: for correct work of Required validation attribute, you should make the underlying property nullable. Without this, the property will be left at its default value (0) after model deserializer. Model validation has no ability to distinguish between missed value and value equal to default one. So for correct work of Required attribute make the property nullable:
public class MyModelClass
{
[Required(ErrorMessage = "{0} is required")]
public int? Level { get; set; }
}
How can I create a custom validation attribute with client side validation without implementing IClientValidatable?
How does System.ComponentModel.DataAnnotations.RequiredAttribute client side validate?
The reason to do this is because I'm using objects from classes in another project as models in my views and I don't want to add the System.Web.MVC reference to that project.
EDIT to add more information:
I know that IClientValidatable is used to add custom attributes to
the HTML to be used later by the unobtrusive validation.
I know I'll need to add the javascript code to made the validation in
the client.
What I don't know is how to use the information from the custom validation attribute to add the necessary attributes to the HTML for unobtrusive validation to work.
This is my custom validation attribute:
public class RequiredGuidAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
Guid? guidValue = value as Guid?;
if (guidValue == null)
return false;
return guidValue != Guid.Empty;
}
}
This is my property with the attribute applied:
[RequiredGuid(ErrorMessageResourceType = typeof(ClientOrderResources), ErrorMessageResourceName = "RequiredShippingMethod")]
public Guid ShippingMethodId
{
get { return GetProperty(ShippingMethodIdProperty); }
set { SetProperty(ShippingMethodIdProperty, value); }
}
And finally I'm rendering a hidden input for that property in the view using Html.HiddenFor.
Now, how can I get the error message from the attribute to apply it to the HTML? Should I do it my self using Reflection or there is a better way?
And then how can I tell Html.HiddenFor to use that information to add the necessary attributes to the HTML?
We had a similar problem. We have a model we use for our account creation that uses IClientValidatable on its custom attributes. However, we created a batch account creation process that sits outside of the website that we weren't able to reference System.Web.Mvc in. Because of this, when we called Validator.TryValidateObject, any custom validator that inherited from IClientValidatable was simply skipped. Here's what we were working with that was failing to validate outside of our website:
public class AgeValidatorAttribute : ValidationAttribute, IClientValidatable
{
public int AgeMin { get; set; }
public int AgeMax { get; set; }
public override bool IsValid(object value)
{
//run validation
}
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = ErrorMessageString,
ValidationType = "agevalidator"
};
rule.ValidationParameters["agemin"] = AgeMin;
rule.ValidationParameters["agemax"] = AgeMax;
yield return rule;
}
Removing System.Web.Mvc required us to also remove GetClientValidationRules and the IClientValidatable reference. In order to do this and still have client side validation, we had to create a new class:
public class AgeValidatorClientValidator : DataAnnotationsModelValidator<AgeValidatorAttribute>
{
private readonly string _errorMessage;
private readonly string _validationType;
public AgeValidatorClientValidator(ModelMetadata metadata, ControllerContext context, AgeValidatorAttribute attribute)
: base(metadata, context, attribute)
{
this._errorMessage = attribute.FormatErrorMessage(metadata.DisplayName);
this._validationType = "agevalidator";
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
var rule = new ModelClientValidationRule
{
ErrorMessage = this._errorMessage,
ValidationType = this._validationType
};
rule.ValidationParameters["agemin"] = base.Attribute.AgeMin;
rule.ValidationParameters["agemax"] = base.Attribute.AgeMax;
yield return rule;
}
}
As you can see, it does essentially the same thing as it used to, it's just done using the DataAnnatotationsModelValidator rather than IClientValidatable. There's one more step we need to do to actually attach the DataAnnotationsModelValidator to the atttribute, and that's done in the Global.asax.cs Application_Start method
DataAnnotationsModelValidatorProvider.RegisterAdapter(
typeof(AgeValidatorAttribute), typeof(AgeValidatorClientValidator));
Now you can use this just as you would use a normal attribute:
[AgeValidator(AgeMax = 110, AgeMin = 18, ErrorMessage = "The member must be between 18 and 110 years old")]
public string DateOfBirth { get; set; }
I know this question is a year old, but I spent all day yesterday and half of today trying to figure this issue out. So I hope this helps somebody who runs into the same problem if OP hasn't figured the answer out yet.
Please note, I did not include any javascript in this writeup as it required no changes from the standard implementation of custom validation rules using jQuery.validate.
You can't have custom validation on the client unless you implement IClientValidatable. And for that you also need to add client script as well.
http://msdn.microsoft.com/en-us/vs2010trainingcourse_aspnetmvccustomvalidation_topic3.aspx
It is possible, i found this article on how to do it:
http://xhalent.wordpress.com/2011/05/12/custom-unobstrusive-jquery-validation-in-asp-net-mvc-3-using-dataannotationsmodelvalidatorprovider/
basically you have to create a DataAnnotationsModelValidator on your client an register it in Application_Start().
And don't forget that you still have to write the Javascript for client side validation.
I have a view model
public class ViewModel
{
public string Text { get; set; }
public string Name { get; set; }
}
The submited form provides only the Text value. I'd like to set the the Name property in my custom model binder.
So I derived my custom model binder from the DefaultModelBinder class and overrided the BindModel method.
The problem is that the BindModel method is called only for the incomning properties.
My question is how can I set the Name value in my cystom model binder ?
If you do not have an incoming value for Name, then you are not doing (custom) model binding. Instead, you want to supply some data in your model object before the action executes, right ? If so, use ActionFilter for it, override OnActionExecuting() and supply the data you need into action parameters.
public class SupplyNameAttribute : FilterAttribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.ActionParameters != null)
{
foreach (KeyValuePair<string, object> parameter in filterContext.ActionParameters)
{
if (parameter.Key == "Name") parameter.Value == "Hey";
}
}
}
}
EDIT :
You can also use custom ValueProvider for default model binding, see
http://mgolchin.net/posts/19/dive-deep-into-mvc-ivalueprovider
I have an object class generated from a T4, with a partial SafeClass to do validation, which looks like this:
public partial class Address : IValidatableObject
This class has a Validate method like so:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
//ValidationResponse is a custom struct that holds a few properties.
ValidationResponse r = this.IsValidAddress(); //a method that does some checks
if (!r.IsValid)
{
yield return new ValidationResult(r.Message, new string[] { "Address1" });
}
}
In my Controller's HttpPost event, I have the line:
if (!TryUpdateModel(_policy))
return View(_policy);
Note that the Policy object contains a Person object, which contains an Address object (pieces of all 3 are rendered in the same view; may be relevant, I don't know).
When TryUpdateModel() executes, the Address's Validate method gets called 3 times. I verified it's not triggering for other addresses on the policy. I have also verified that the Controller's HttpPost method is only being called once. It's the single execution of TryUpdateModel() that fires off 3 Validates.
Has anybody else run into this sort of thing? Any ides what's going on?
I had encoutered similar issue running this code
if (isPoorSpecimen)
{
errors.Add(new ValidationResult("Your are reporting poor specimen condition, please specify what is the reason"
, new string[] { "SpecimenCondition", "QtyOk", "DocumentedOk", "ColdChainOk", "PackagingOK", "IsProcessable" }));
}
It will show the error message 6 times in Html.ValidationSummary() .
The solution is to highligt a single control per error.
if (isPoorSpecimen)
{
errors.Add(new ValidationResult("Your are reporting poor specimen condition, please specify what is the reason"
, new string[] { "SpecimenCondition" }));
}
It is called 3 times, because the Address instance is validated first as a standalone entity, then as a member of a standalone Person entity, and finally as a member of the Person entity being a member of the Policy entity.
I would suggest the following solutions:
1) Remove IValidatableObject from all the classes but Policy and validate its members manually:
public class Policy : IValidatableObject
{
public Person PersonInstance { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// validate Policy...
// then validate members explicitly
var results = PersonInstance.Validate(validationContext);
}
}
public class Person
{
public Address AddressInstance { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// validate Person...
// then validate members explicitly
var results = AddressInstance.Validate(validationContext);
}
}
public class Address
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// validate Address...
}
}
2) Or add a flag to each class to validate only once, since the instance across the calls is the same:
private bool validated = false;
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!validated)
{
validated = true;
// do validation
}
}
Hope this helps.
I know this is probably not possible but let's say I have a model with two properties.
I write a ValidationAttribute for one of the properties. Can that VA look at the other property and make a decision?
So;
public class QuickQuote
{
public String state { get; set; }
[MyRequiredValidator(ErrorMessage = "Error msg")]
public String familyType { get; set; }
So in the above example, can the validator test to see what's in the "state" property and take that into consideration when validating "familyType"?
I know I can probably save the object to the session but would like to avoid any saving of state if possible.
Your custom validation could be applied to the class directly, take a look at PropertiesMustMatch attribute in the AccountModels class that is created by default as a part of the MVC project template in VS2008.
Another way to achieve this kind of validation is to have your model implement IDataErrorInfo. That way you can do whole viewmodel validation.
This page has some information about iplementing the IDataErrorInfo Interface, about 2/3 of the way down under the heading "mplementing the IDataErrorInfo Interface"
Use ValidationContext to get your model:
public class MyRequiredValidator: RequiredAttribute
{
public override bool RequiresValidationContext
{
get {return true;} //it needs another propertie in model
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
QuickQuote model = (QuickQuote)validationContext.ObjectInstance;
if (model.state == "single")
return null;
else
return base.IsValid(value, validationContext);//familyType is require for married
}
}