0

We used DB-first approach to generate models in a .NET core application. DataAnnotations were put in a "buddy" metadata class so as to avoid writing in an autogenerated file. When controller calls TryValidateModel, all works well, Name property is required.

public partial class User
{
    public string Name { get; set; }
}

[ModelMetadataType(typeof(UserMetaData))]
public partial class User : IValidatableObject
{
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { }
}

public class UserMetaData
{
    [Required]
    public string Name { get; set; }
}

On a service layer of the app, we want to implement additional validation, that also checks if objects are valid in regards to data annotations. This is done via Validator.TryValidateObject() which successfully calls Validate method, but disregards data annotations - user is valid, even with an empty name.

TL;DR: MVC (web project) knows how to consider data annotations put in a "buddy" class via ModelMetadataType attribute, service layer project does not.

I thought i have found the answer here, but it seems that TypeDescriptor.AddProviderTransparent does not work for .net core apps.

Any ideas would be greatly appreciated.

berserker
  • 104
  • 5

1 Answers1

1

I really hoped for a one line solution :)

I abused ashrafs answer to his own question like so:

var metadataAttr = typeof(T).GetCustomAttributes(typeof(ModelMetadataTypeAttribute), true).OfType<ModelMetadataTypeAttribute>().FirstOrDefault();
if (metadataAttr != null)
{
    var metadataClassProperties = TypeDescriptor.GetProperties(metadataAttr.MetadataType).Cast<PropertyDescriptor>();
    var modelClassProperties = TypeDescriptor.GetProperties(typeof(T)).Cast<PropertyDescriptor>();

    var errs =
        from metaProp in metadataClassProperties
        join modelProp in modelClassProperties
        on metaProp.Name equals modelProp.Name

        from attribute in metaProp.Attributes.OfType<ValidationAttribute>()
        where !attribute.IsValid(modelProp.GetValue(model))
        select new ValidationResult(attribute.FormatErrorMessage(Reflector.GetPropertyDisplayName<T>(metaProp.Name)), new[] { metaProp.Name });

    validationResults.AddRange(errs);
}
berserker
  • 104
  • 5