Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

How to get a validation attr from model by property name? #5240

Closed
jsdmitry opened this issue Sep 6, 2016 · 11 comments
Closed

How to get a validation attr from model by property name? #5240

jsdmitry opened this issue Sep 6, 2016 · 11 comments

Comments

@jsdmitry
Copy link

jsdmitry commented Sep 6, 2016

I use the validator.GetClientValidationRules() method that returns validation parameters from the ModelClientValidationRule class. And, It works perfectly for all validation attributes, but sadly not in the ASP.Net Core. Is there some public API I could use to get validation parameters from a validation attribute in ASP.Net Core?

My model:

public class Movie {
    public int ID { get; set; }

    [StringLength(60, MinimumLength = 3)]
    public string Title { get; set; }

    [Display(Name = "Release Date")]
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$")]
    [Required]
    [StringLength(30)]
    public string Genre { get; set; }

    [Range(1, 100)]
    [DataType(DataType.Currency)]
    public decimal Price { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$")]
    [StringLength(5)]
    public string Rating { get; set; }
}

This is my method that generates my client validation rules by the property name of the model:

IEnumerable<IDictionary<string, object>> GetValidationRulesByPropertyName(string propertyName) {
        IList<IDictionary<string, object>> clientValidationRules = new List<IDictionary<string, object>>();

        ModelMetadata modelMetaData = ModelMetadata.FromStringExpression(propertyName, this.ViewContext.ViewData);
        IEnumerable<ModelValidator> validators = ModelValidatorProviders.Providers.GetValidators(modelMetaData, this.ViewContext);
        foreach(ModelValidator validator in validators) {
            foreach(ModelClientValidationRule rule in validator.GetClientValidationRules()) {
                IDictionary<string, object> clientValidationRule = new Dictionary<string, object>() {
                    ["type"] = rule.ValidationType,
                    ["message"] = rule.ErrorMessage
                };
                rule.ValidationParameters.AddTo(clientValidationRule);
                clientValidationRules.Add(clientValidationRule);
            }
        }

        return clientValidationRules;
    }
}
@dougbu
Copy link
Member

dougbu commented Sep 6, 2016

What you've got will work.

We're planning to address #5028 in 1.1.0. This bug appears to duplicate that request. Please reopen if you disagree.

@dougbu dougbu closed this as completed Sep 6, 2016
@dougbu
Copy link
Member

dougbu commented Sep 7, 2016

OP in #5240 provided his workaround for pre-1.1.0.

@dougbu
Copy link
Member

dougbu commented Sep 7, 2016

Oops, comment was meant for #5028.

@akonyer
Copy link

akonyer commented Sep 7, 2016

I have been trying to get this to work, without success. I am trying to get a list of validators using the following code, but this method does not seem to exist.

IEnumerable<ModelValidator> validators = ModelValidatorProviders.Providers.GetValidators(modelMetaData, this.ViewContext);

I think the problem is that I am trying to do this server side instead of in a cshtml file, so I can not use ModelValidatorProviders.Providers.GetValidators.

Should I instead be using MvcOptions.ModelValidatorProviders?

@dougbu
Copy link
Member

dougbu commented Sep 7, 2016

Should I instead be using MvcOptions.ModelValidatorProviders?

Yes. Well, the ClientModelValidatorProviders since you're looking for something similar to validator.GetClientValidationRules().

I spoke too quickly when I said "What you've got will work." The general idea is fine but the details look like an earlier release. The current DefaultHtmlGenerator code to add client validation attributes is

var clientValidatorProviders = optionsAccessor.Value.ClientModelValidatorProviders;
_clientModelValidatorProvider = new CompositeClientModelValidatorProvider(clientValidatorProviders);
// ...
var validators = _clientValidatorCache.GetValidators(modelExplorer.Metadata, _clientModelValidatorProvider);
if (validators.Count > 0)
{
    var validationContext = new ClientModelValidationContext(
        viewContext,
        modelExplorer.Metadata,
        _metadataProvider,
        tagBuilder.Attributes);

    for (var i = 0; i < validators.Count; i++)
    {
        var validator = validators[i];
        validator.AddValidation(validationContext);
    }
}

You can do the same thing w/ a Dictionary<string, string> instead of the Attributes of a TagBuilder.

@omuleanu
Copy link

omuleanu commented Sep 7, 2016

this is what I do right now:

var content = html.TextBox(prop);
var tagBuilder = content as TagBuilder;

var vs = tagBuilder.Attributes
    .Where(item => item.Key.StartsWith("data-", StringComparison.OrdinalIgnoreCase))
    .ToDictionary(item => item.Key, item => item.Value);

@jsdmitry
Copy link
Author

jsdmitry commented Sep 7, 2016

OP in #5240 provided his workaround for pre-1.1.0.

Please give me more detailed information about a workaround. I can not find his in #5240

@omuleanu
Copy link

omuleanu commented Sep 7, 2016

@jsdmitry I just did ^

@jsdmitry
Copy link
Author

jsdmitry commented Sep 7, 2016

I just did ^

Our html helpers generate the input tag on the client side and so "data-" attributes are not applied to the input element. For this reason, I cannot use your workaround.

@akonyer
Copy link

akonyer commented Sep 7, 2016

Thank you very much guys. Obviously I'm still getting familiar with the new framework. This is perfect.

@akonyer
Copy link

akonyer commented Sep 7, 2016

Yes, I can not use @omuleanu workaround either, as the validation data attributes are not yet applied to the input element.

I'm working with the code provided by @dougbu and I think I can make this work. It's unfortunate because I want to use this in a static class, so I can not take advantage of dependency injection to get access to some of those classes. But will work with what I have. Thanks for your all your help.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants