0

Hello I am facing a small design problem with my application (.NET Core 2.2 API).

I want to create a custom ValidationAttribute that would be able to check if a certain property matches a Regex pattern.

What differs this attribute from built-in RegularExpressionAttribute is that patterns will be stored in the DB lookup table in format [column_name|regex_patter].

For simplicity, let's say that a class that is used to load this lookup table is called IDataRepository and a method that returns that dictionary is called LoadRegexPatterns()

I would like to be able to use the attribute like:

[RegexPattern("human_name")]
public string FirstName {get;set;}

[RegexPattern("human_name")]
public string LastName {get;set;}

I am not sure which place would be the best to load the lookoup dictionary and how to handle passing it, in the way custom ValidationAttribute could use it. I would appreciate any tips.

  • I can't use IDataRepository inside an attribute class, because I would have to inject it there.

  • Maybe a "singleton lookup dictionary" with values from the BD? But still - how to handle it inside the Startup class so it could be used by ValidationAttribute

public sealed class RegexPatternAttribute : ValidationAttribute { private readonly string _columnName;

public RegexPatternAttribute(string columnName)
{
    _columnName = columnName;
}

protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
    // var regexPattern = LOAD REGEX PATTERN SHOULD BE LOADED HERE FROM SOME KIND OF LOOKUP TABLE
    string inputValue = value.ToString();

    if (Regex.IsMatch(inputValue,regexPattern))
    {
        return ValidationResult.Success;
    }
    else
    {
        return new ValidationResult(ErrorMessage);
    }
}

}

1

1 Answer 1

0

Since ValidationContext implements IServiceProvider, you can use its GetService method to retrieve any registered dependency from within an attributes IsValid method.

Note that you can retrieve any service, e.g. one (in StartUp.cs) preconfigured with the data from the database.
The ValidationContext as a IServiceProvider offers a means to retrieve data that can't be configured via the properties of an attribute.

protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
    var dataRepository = validationContext.GetService(typeof(IDataRepository ));    
    
    // ...  
}

Make sure you have that IDataRepository registered in your container.

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddScoped<IDataRepository, DataRepository>();
        // or any other lifetime scope.

        // ...

    }
}
Sign up to request clarification or add additional context in comments.

5 Comments

I will check that in a second, but I already see the problem with this approach. It would call the Database on each Attribute check. There is no need for that in my case, as this lookup data will not change frequently.
You van keep a field in the attribute with the data you need, only when it is null you call that repository.
Don't use service locator pattern in validation attributes. I wouldn't call the db either. You're just setting yourself up for trouble. What if there's an exception? Then what do you do? Intercept your route config and redirect and blahh...The best approach is to validate inside the controller, add any errors to the model state, and then check if the model state is valid.
@GHDevOps How does that differ when an exception occurs? Both setups will end up in an error, or at best in an exception handler middleware if one is present.
I've used service location in my validation attributes for years, no issues.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.