Custom Validation Attribute: Comparing one property to another property's inner property - c#

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

How to pass a field or dynamically changing value to a custom validator in C#?

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?

How to put conditional Required Attribute into class property to work with WEB API?

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; }
}

How to access viewmodel's property value in custom validation attribute to alter messages?

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.

how to write non-zero expression for annotation in meta data for wcf ria service?

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)
);
}
}

Read value of another property in asp.net mvc2 custom attribute?

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();
...
}
}

Categories

Resources