Reputation: 14713
I built a custom ValidationAttribute so I can validate unique email addresses in my system. However, I'd like to pass in a custom parameter somehow to add more logic to my validation.
public class UniqueEmailAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
//I need the original value here so I won't validate if it hasn't changed.
return value != null && Validation.IsEmailUnique(value.ToString());
}
}
Upvotes: 24
Views: 23304
Reputation: 1245
Like this?
public class StringLengthValidatorNullable : ValidationAttribute
{
public int MinStringLength { get; set; }
public int MaxStringLength { get; set; }
public override bool IsValid(object value)
{
if (value == null)
{
return false;
}
var length = value.ToString().Length;
if (length < MinStringLength || length >= MaxStringLength)
{
return false;
}
return true;
}
}
Use:
[StringLengthValidatorNullable(MinStringLength = 1, MaxStringLength = 16)]
public string Name {get; set;}
Upvotes: 62
Reputation: 2624
You could also pass parameters that are other properties in the same model.
Create a custom validation attribute:
public class SomeValidationAttribute : ValidationAttribute
{
//A property to hold the name of the one you're going to use.
public string OtherProperty { get; set; }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
//Get the value of the property using reflection.
var otherProperty = validationContext.ObjectType.GetProperty(OtherProperty);
var otherPropertyValue = (bool)otherProperty.GetValue(validationContext.ObjectInstance, null);
if (value != null && otherPropertyValue)
{
return ValidationResult.Success;
}
return new ValidationResult("Invalid property message.");
}
}
Then pass the name of the property you're going to use.
public class RequestModel
{
public bool SomeProperty { get; set; }
[SomeValidation(OtherProperty = "SomeProperty")]
public DateTime? StartDate { get; set; }
}
Upvotes: 7
Reputation: 4212
Had a similar requirement where I had to pass along a value to a custom attribute.
The issue here is that Attribute decorations don't allow variables. You get compile time error:
An object reference is required for the non-static field, method, or property
Here is how I was able to do it:
In Controller
[FineGrainAuthorization]
public class SomeABCController : Controller
{
public int SomeId { get { return 1; } }
}
In Attribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class FineGrainAuthorizationAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
ControllerBase callingController = filterContext.Controller;
var someIdProperty = callingController.GetType().GetProperties().Where(t => t.Name.Equals("SomeId")).First();
int someId = (int) someIdProperty.GetValue(callingController, null);
}
}
Remember that the string inside .Name.Equals("SomeId")
must case match the declaration public int SomeId
Upvotes: 1
Reputation: 7569
I would suggest something like @Oliver's answer (that's what I'd do, anyway), but then I noticed that you do not want to pass constants.
What about something like this, then?
public static class MyConfig
{
public static int MinStringLength { get; set; }
public static int MaxStringLength { get; set; }
public static SomeObject otherConfigObject { get; set; }
}
And then access MyConfig.MinStringLength
from validation?
I'll agree that it isn't too pretty, though.
Upvotes: 0