DisplayName attributes not showing in error message when no rule created for model property

Jun 20, 2013 at 12:57 AM
Edited Jun 20, 2013 at 1:03 AM
This is cross-posted from a less detailed question on Stack Overflow:

I'm using FluentValidation.MVC4, and if I have a display name for a property on my model class, but no rule configured in its validator, the model errors collection does not make use of the configured display name - instead, it just splits the property name itself.

If I add a rule, the display name is used.
[DisplayName("Site")]
public int SiteId { get; set; }
Error message with no rule configured for SiteId: "'Site Id' must not be empty."
Error message with "not empty" rule configured for SiteId: "'Site' must not be empty."

I think I've tracked this down to the constructor in FluentValidationPropertyValidator (in the FluentValidation.Mvc project), which either allows the display name to be used based on the incoming rule, however when no rule is configured in the validator the incoming rule parameter is null, and so FluentValidationPropertyValidator (as it stands) has no way to get the display name (even though it is available on the incoming metadata parameter).

I've written some code that seems to produce the behaviour I'm expecting, but it's in the FluentValidationPropertyValidator class itself, and I really don't want to it this way:
public FluentValidationPropertyValidator(ModelMetadata metadata, ControllerContext controllerContext, PropertyRule rule, IPropertyValidator validator) : base(metadata, controllerContext) {
  this.Validator = validator;

  // Build a new rule instead of the one passed in.
  // We do this as the rule passed in will not have the correct properties defined for standalone validation.
  // We also want to ensure we copy across the CustomPropertyName and RuleSet, if specified. 
  /*
   * Original behaviour:
   * 
  Rule = new PropertyRule(null, x => metadata.Model, null, null, metadata.ModelType, null) {
    PropertyName = metadata.PropertyName,
    DisplayName = rule == null ? null : rule.DisplayName,
    RuleSet = rule == null ? null : rule.RuleSet
  };
   */

  // New behaviour 
  IStringSource displayNameSource;
  if (rule == null)
  {
    displayNameSource = string.IsNullOrEmpty(metadata.DisplayName) ? null : new LazyStringSource(() => metadata.DisplayName);
  }
  else
  {
    displayNameSource = rule.DisplayName;
  }

  // Play nicely with data annotations when no rule is specified!
  Rule = new PropertyRule(null, x => metadata.Model, null, null, metadata.ModelType, null)
  {
    PropertyName = metadata.PropertyName,
    DisplayName = displayNameSource,
    RuleSet = rule == null ? null : rule.RuleSet
  };
  // End of new behaviour 
}
Is there a better way to customise this behaviour so FluentValidation can use the display name when no rule is specified in the validator (i.e. such as when FluentValidation is dealing with a 'required' field)?
Coordinator
Jun 20, 2013 at 9:26 AM
Thanks for the code - this is probably the best way to do it (although it really only applies to required fields due to the strange way MVC handles them, so it might be better in the Required property validator) I'll try and get this incorporated sometime in the next few days.

Jeremy