Mike Cole
Mike Cole

Reputation: 14713

Passing custom parameters to ValidationAttribute

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

Answers (4)

Oliver
Oliver

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

typhon04
typhon04

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

joym8
joym8

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

Joel
Joel

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

Related Questions