I have a class StarActivityModel, and I want to validate that the inputted value for StarChange, is less than the Client's property of StarCount. To do this I have attempted to create a Custom Validation Attribute but am having trouble getting the StarCount value.
public class StarActivityModel : BaseModel
{
[Display(Name = "App User")]
public Client? Client { get; set; }
[Display(Name = "Star Change")]
public int? StarChange { get; set; }
}
public class Client
{
public virtual int StarCount { get; set; }
}
My attempt at a custom Validation Attribute
[AttributeUsage(AttributeTargets.Property)]
public class ValidStarChangeAttribute : ValidationAttribute
{
private readonly string _comparisonProperty;
public ValidStarChangeAttribute(string testedPropertyName)
{
_comparisonProperty = testedPropertyName;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var propertyInfo = validationContext.ObjectType.GetProperty(_comparisonProperty);
//Compare passed StarCount and Client starcount
if((int) value > //Somehow get client StarCount)
return //Code an error message
return ValidationResult.Success;
}
}
You could search the ValidationContex class for the method and property you need in custom model validation.
I modified your codes and it seems work well
Codes:
public class ValidStarChangeAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
StarActivityModel staractivemodel = (StarActivityModel)validationContext.ObjectInstance;
if (value != null)
{
if((int)value< staractivemodel.Client.StarCount)
{
return ValidationResult.Success;
}
}
return new ValidationResult("error");
}
}
Result:
Related
I have a class
public class File {
[Required, MinLength(1, ErrorMessage = "The Header should contain at least one element")]
public List<string> Header { get; set; }
[ColumnLenghts]
public List<int> ColumnLenghts { get; set; }
//other fields
}
and in my custom validator I want to check
public class ColumnLenghtsAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
List<int> columnLengths = (List<int>) value;
// other checks
if(columnLengths.Count != Header.Count) {
return new ValidationResult(" number of column length does not match to headers");
}
return ValidationResult.Success;
}
}
Problem:
I could not find a way to pass Header field to the custom validator.
I have tried
public class File {
[Required, MinLength(1, ErrorMessage = "The Header should contain at least one element")]
public List<string> Header { get; set; }
[ColumnLenghts (headerCount = Header.Count)]
public List<int> ColumnLenghts { get; set; }
//other fields
}
public class ColumnLenghtsAttribute : ValidationAttribute
{
public int headerCount {get; set;}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
List<int> columnLengths = (List<int>) value;
// other checks
if(columnLengths.Count != headerCount) {
return new ValidationResult(" number of column length does not match to headers");
}
return ValidationResult.Success;
}
}
and I am getting the error: an object reference is required for the non-static field
Question: Is it possible to pass a field or maybe dynamically changing value to a custom validator?
I just want to put conditional Required Attribute which is work with WEB API
Example
public sealed class EmployeeModel
{
[Required]
public int CategoryId{ get; set; }
public string Email{ get; set; } // If CategoryId == 1 then it is required
}
I am using Model State validation via (ActionFilterAttribute)
You can implement your own ValidationAttribute. Perhaps something like this:
public class RequireWhenCategoryAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var employee = (EmployeeModel) validationContext.ObjectInstance;
if (employee.CategoryId == 1)
return ValidationResult.Success;
var emailStr = value as string;
return string.IsNullOrWhiteSpace(emailStr)
? new ValidationResult("Value is required.")
: ValidationResult.Success;
}
}
public sealed class EmployeeModel
{
[Required]
public int CategoryId { get; set; }
[RequireWhenCategory]
public string Email { get; set; } // If CategoryId == 1 then it is required
}
This is just a sample. It may have casting issues, and I'm not sure this is the best approach to solve this problem.
Here's my 2 cents. It will give you a nice message like "AssigneeId is required for the current AssigneeType value Salesman" It works for enums too.
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class RequiredForAnyAttribute : ValidationAttribute
{
/// <summary>
/// Values of the <see cref="PropertyName"/> that will trigger the validation
/// </summary>
public string[] Values { get; set; }
/// <summary>
/// Independent property name
/// </summary>
public string PropertyName { get; set; }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var model = validationContext.ObjectInstance;
if (model == null || Values == null)
{
return ValidationResult.Success;
}
var currentValue = model.GetType().GetProperty(PropertyName)?.GetValue(model, null)?.ToString();
if (Values.Contains(currentValue) && value == null)
{
var propertyInfo = validationContext.ObjectType.GetProperty(validationContext.MemberName);
return new ValidationResult($"{propertyInfo.Name} is required for the current {PropertyName} value {currentValue}");
}
return ValidationResult.Success;
}
}
Use it like this
public class SaveModel {
[Required]
public AssigneeType? AssigneeType { get; set; }
[RequiredForAny(Values = new[] { nameof(AssigneeType.Salesman) }, PropertyName = nameof(AssigneeType))]
public Guid? AssigneeId { get; set; }
}
The viewmodel has many string properties like Sample as below. My requirement is to show different validation messages depending on a bool flag in my viewmodel. That flag is IsProposer property as mentioned below:
[SampleAttribute(true, "bla prop", "foo add driver")]
public string Sample { get; set; }
public bool IsProposer { get; set; }
I thought to create a validation attribute so that I can just place it on all my string properties (required validation). And then depending on the value of that boolean flag, I will pass the msg accordingly. My custom validation attribute is as follows:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
public class SampleAttribute : RequiredAttribute
{
protected string ProposerErrorMessage { get; set; }
protected string AdditionalDriverErrorMessage { get; set; }
protected bool IsProposer { get; set; }
public SampleAttribute(bool isProposer, string propmsg, string adddrivermsg)
{
ProposerErrorMessage = propmsg;
IsProposer = isProposer;
AdditionalDriverErrorMessage = adddrivermsg;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (IsValid(value))
{
return ValidationResult.Success;
}
else
{
return new ValidationResult(IsProposer ? ProposerErrorMessage : AdditionalDriverErrorMessage);
}
}
}
Now the issue is, as you can see I am just passing true as first parameter for the attribute. Here, I need to pass the Isproposer property's value from the viewmodel instance so that I can then act accordingly. How can I access it?
I solved my problem by creating a attribute like this:
/// <summary>
/// This validation attribute is an extension to RequiredAttribute that can be used to choose either of the two
/// validation messages depending on a property in the context of same model.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
public class RequiredExtensionAttribute : RequiredAttribute
{
private string _errorMessageIfTruthy;
private string _errorMessageIfFalsy;
private string _dependentProperty;
public RequiredExtensionAttribute(string dependentproperty, string errorMessageIfTruthy, string errorMessageIfFalsy)
{
_errorMessageIfTruthy = errorMessageIfTruthy;
_dependentProperty = dependentproperty;
_errorMessageIfFalsy = errorMessageIfFalsy;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var propertyTestedInfo = validationContext.ObjectType.GetProperty(this._dependentProperty);
if (propertyTestedInfo == null)
{
return new ValidationResult(string.Format("unknown property {0}", this._dependentProperty));
}
var propertyTestedValue = propertyTestedInfo.GetValue(validationContext.ObjectInstance, null);
if (IsValid(value))
{
return ValidationResult.Success;
}
else
{
return new ValidationResult((bool)propertyTestedValue ? _errorMessageIfTruthy : _errorMessageIfFalsy);
}
}
}
This can now be used in models like:
[RequiredExtensionAttribute("IsProposerViewModel", "Please select your employment status.", "Please select this driver's employment status")]
public string EmploymentStatus { get; set; }
public bool IsProposerViewModel { get; set; }
-where the first parameter for attribute is the IsProposerViewModel, the dependent value.
Suppose I have a entity property like:
public decimal Amount { get; set; }
then validation can be added in metadata class like:
[Range(0.01, double.MaxValue, ErrorMessage = "Amount is required")]
public decimal Amount { get; set; }
How are about for non-zero request?
You could implement a custom validation attribute. Example:
class MyObject {
[NonZero]
public decimal Amount { get; set; }
}
public class NonZeroAttribute : ValidationAttribute
{
public override string FormatErrorMessage(string name)
{
return string.Format("{0} must be non-zero", name);
}
public override bool IsValid(object value)
{
var zero = Convert.ChangeType(0, value.GetType());
return !zero.Equals(value);
}
protected override ValidationResult IsValid(
object value, ValidationContext validationContext)
{
if (IsValid(value))
return new ValidationResult(null);
else
return new ValidationResult(
FormatErrorMessage(validationContext.MemberName)
);
}
}
I have made custom attribute in my asp.net mvc2 project:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class IsUsernameValidAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
if (value == null)
{
return true;
}
var username = value.ToString();
return UserBusiness.IsUsernameValid(username)
// && value of OtherProperty == true;
}
}
for the model:
public class MyClass
{
[IsUsernameValid]
public string UserName { get; set; }
public bool OtherProperty { get; set; }
}
I can get value of UserName, but can I get value of OtherProperty inside custom attribute and use it in return clause and how. Thanks in advance.
The only way to do this is with a class level attribute. This is often used for validating the Password and PasswordConfirmation fields during registration.
Grab some code from there as a starting point.
[AttributeUsage(AttributeTargets.Class)]
public class MatchAttribute : ValidationAttribute
{
public override Boolean IsValid(Object value)
{
Type objectType = value.GetType();
PropertyInfo[] properties = objectType.GetProperties();
...
}
}