Dmytro
Dmytro

Reputation: 17176

How to get property name by one of it's attributes' value using reflection or how to get property info of data property which is currently validating?

I want to write custom validation attribute and add additional member names which have validation errors to validation result. The thing is I want to generate member name dynamically based on property name and invalid match property index or key (I want to validate IEnumerables or IDictionaries) like Names[0], Names[1], Names[key] etc. For example:

Model:

public class ModelClass
{
    [ItemMaxLength(10)]
    [Display(ResourceType = typeof(CategoriesRename), Name = "CategoryNamesFieldName")]
    public IDictionary<string, string> Names { get; set; }
}

Attribute:

public class ItemMaxLengthAttribute : ValidationAttribute
{
    private readonly int _maxLength = int.MaxValue;

    public ItemMaxLengthAttribute(int maxLength)
    {
        _maxLength = maxLength;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        ...
        // I can get instance and it's type from validation context
        var instance = validationContext.ObjectInstance; // which is instance of ModelClass
        var instanceType = validationContext.ObjectType; //which is typeof(ModelClass)
        var dispayName = validationContext.DisplayName; //which is value of Display attribute
        ...
    }
}

So the main idea is (I don't like it ether) get current property been validated by it's DysplayName attribute value (dispayName). I'm kind'a stuck here for a while. Maybe is there some other way to get property info of the property which is validating?

P.S. I've already tried MemberName property, as Alexandre Rondeau suggested, but the problem is that validationContext.MemberName = null so it can't be used. Also MSDN says that this property represents an entity member name, not the name of a corresponding data field and I need the name of a corresponding data field.

Upvotes: 6

Views: 9182

Answers (2)

ivanko337
ivanko337

Reputation: 389

You have to use validationContext.MemberName property.

Example:

protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
        var userManager = validationContext.GetService<UserManager<ApplicationUser>>();
        
        var findingTask = userManager.FindByEmailAsync((string)value);
        findingTask.Wait();

        var user = findingTask.Result;

        return user == null
                ? ValidationResult.Success
                : new ValidationResult("This email already in use", new string[] { validationContext.MemberName });
}

Upvotes: 0

Alexandre Rondeau
Alexandre Rondeau

Reputation: 2687

Using that code, both test passes, so the MemberName isn't null.

[TestClass]
public class RefectionInValidationTest
{
    [TestMethod]
    public void GivenAModelWithItemMaxAttributeOnFieldName_WhenValidating_ThenModelClassIsValid()
    {
        //Arange
        var validModelClass = new ModelClass();
        var validations = new Collection<ValidationResult>();

        //Act
        var isValid = Validator.TryValidateObject(validModelClass, new ValidationContext(validModelClass, null, null), validations, true);

        //Assert
        Assert.IsTrue(isValid);
    }

    [TestMethod]
    public void GivenAModelWithItemMaxAttributeOnFieldNotName_WhenValidating_ThenModelClassIsInvalid()
    {
        //Arange
        var invalidaModelClass = new InvalidModelClass();
        var validations = new Collection<ValidationResult>();

        //Act
        var isValid = Validator.TryValidateObject(invalidaModelClass, new ValidationContext(invalidaModelClass, null, null), validations, true);

        //Assert
        Assert.IsFalse(isValid);
    }
}

public class ModelClass
{
    [ItemMaxLength(10)]
    public IDictionary<string, string> Names { get; set; }
}
public class InvalidModelClass
{
    [ItemMaxLength(10)]
    public IDictionary<string, string> NotNames { get; set; }
}

public class ItemMaxLengthAttribute : ValidationAttribute
{
    private readonly int _maxLength = int.MaxValue;

    public ItemMaxLengthAttribute(int maxLength)
    {
        _maxLength = maxLength;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var propretyInfo = validationContext.ObjectType.GetProperty(validationContext.MemberName);
        if (propretyInfo.Name == "Names")
            return ValidationResult.Success;

        return new ValidationResult("The property isn't 'Names'");
    }
}

Upvotes: 5

Related Questions