0

I'm developing ASP.NET MVC appliation. I've found Fluent Validation great validation tool and it works, but with my current architecture it has one drawback. The validator does not care about Metadata. I'm using Metadata on seperate class for clarity.

Model

[MetadataType(typeof(DocumentEditMetadata))]
[Validator(typeof(DocumentValidator))]
public class DocumentEditModel
{
    public string DocumentNumber { get; set; }
    (etc...)
}

Metadata Model

public class DocumentEditMetadata
{
    [Required]
    [StringLength(50)]
    [Display(ResourceType = typeof(Label), Name = "DocumentNumber")]
    public string DocumentNumber { get; set; }
    (etc...)
}

Can anyone point a solution? I need data annotations for localization of labels (hence the DisplayAttribute).

Hefass
  • 161
  • 2
  • 8

2 Answers2

1

Think you need to write your own Display name resolver for fluent validation (guess this should be placed in your global.asax).

Caution

This solution is only trying to resolve the display name.

Your other "validation" attributes (Required, StringLength) should no more be used, as you will manage that with FluentValidation.

ValidatorOptions.DisplayNameResolver = (type, memberInfo, expression) =>
{
      //this will get in this case, "DocumentNumber", the property name. 
      //If we don't find anything in metadata / resource, that what will be displayed in the error message.
      var displayName = memberInfo.Name;
      //we try to find a corresponding Metadata type
      var metadataType = type.GetCustomAttribute<MetadataTypeAttribute>();
      if (metadataType != null)
      {
           var metadata = metadataType.MetadataClassType;
           //we try to find a corresponding property in the metadata type
           var correspondingProperty = metadata.GetProperty(memberInfo.Name);
           if (correspondingProperty != null)
           {
                //we try to find a display attribute for the property in the metadata type
                var displayAttribute = correspondingProperty.GetCustomAttribute<DisplayAttribute>();
                if (displayAttribute != null)
                {
                     //finally we got it, try to resolve the name !
                     displayName = displayAttribute.GetName();
                }
          }
      }
      return displayName ;
};

Personal point of view

By the way, if you just use Metadata classes for clarity, don't use them ! It may be a solution if you have no choice (when entity classes are generated from an edmx and you really want to manage the display names this way), but I would really avoid them if it's not necessary.

Raphaël Althaus
  • 59,727
  • 6
  • 96
  • 122
  • Thanks for helpful response and the opinion. It works for rules, but when I don't specify a rule (not needed for not nullables) it uses default resolver. I cant' find similar tweak for the case – Hefass Sep 16 '16 at 13:00
  • It seems that FluentValidationModelValidatorProvider adds "required" if needed but user does not specified it him self (in fluent). Unfortunately in ctor of FluentValidationPropertyValidator there is default logic which creates PropertyRule, and than it overrides DisplayName properly set in PropertyRule ctor. I see it as a bug. https://github.com/JeremySkinner/FluentValidation/blob/11e46cba4727f2e7e0a06de51b4d0e4bd92ba341/src/FluentValidation.Mvc3/PropertyValidatorAdapters/FluentValidationPropertyValidator.cs#L30 – Hefass Sep 16 '16 at 15:15
  • You mean that the problem is when you put a null value for a non nullable property ? This is not related to FluentValidation, but default model binder from Mvc. – Raphaël Althaus Sep 16 '16 at 15:33
  • It's not what I meant. This is wanted behavior. The problem is that FluentValidation logic does not use default DisplayNameResolver but the hardcoded _defulatDisplayNameResolver (check the reference). As a result I get default display name (e.g. "Document Number" for property DocumentNumber) instead of firing ValidatorOptions.DisplayNameResolver I've specified beforehand. – Hefass Sep 19 '16 at 08:46
  • You may take a look at this, maybe : http://stackoverflow.com/questions/9746186/validation-type-names-in-unobtrusive-client-validation-rules-must-be-unique – Raphaël Althaus Sep 20 '16 at 07:44
0
public class CreateHireViewModel 
{
    [Display(Name = nameof(CreateHireViewModel.Title), ResourceType = typeof(Resource.HireResource.Hire))]
    public string Title { get; set; }
}

public class CreateHireViewModelValidator : AbstractValidator<CreateHireViewModel>
{
    public CreateHireViewModelValidator(IStringLocalizer<Resource.HireResource.Hire> l)
    {
        RuleFor(x => x.Title).NotEmpty().WithName(l[nameof(CreateHireViewModel.Title)]); 
        
        RuleFor(x => x.Title).Length(3, 50).WithName(l[nameof(CreateHireViewModel.Title)]);

    }
}
Behrouz
  • 1
  • 4
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Nov 30 '22 at 00:50