Добавление DataAnnotation в класс при использовании FluentValidation - PullRequest
2 голосов
/ 28 апреля 2011

Я использую инфраструктуру FluentValidation для добавления проверки и аннотаций к моим моделям в проекте MVC.

Мне нужно добавить аннотации данных к уровню класса модели. А именно, модель должна иметь добавленный атрибут DisplayColumn. Но, поскольку я использую FluentValidation (и для приложения ModelMetadataProvider установлено использование FluentValidation), даже если я добавлю атрибут DisplayColumn в класс модели, он не используется. Однако я не могу найти способ добавить эту аннотацию с помощью FluentValidation.

Кто-нибудь знает, как я могу заставить это работать?

Спасибо

1 Ответ

4 голосов
/ 28 апреля 2011

На самом деле я бы не рекомендовал использовать FluentValidationModelMetadataProvider - это действительно было только экспериментальным дополнением (которое очень хорошо может быть удалено из следующего выпуска), и оно не поддерживает какие-либо аннотации уровня данных класса (такие какDisplayColumn).Я хотел бы предложить, чтобы вы использовали FluentValidation только для проверки, но придерживались атрибутов для метаданных.

При этом, если вы действительно хотите, чтобы это работало, вы могли бы реализовать это с помощью пользовательского бездействующего валидатора, используемоготолько для метаданных:

public static class MetadataExt {
    public static IRuleBuilderOptions<T, TProperty> DisplayColumn<T, TProperty>(this IRuleBuilder<T, TProperty> rule) {
        var ruleBuilder = (FluentValidation.Internal.RuleBuilder<T, TProperty>)rule;
        ruleBuilder.Rule.AddValidator(new DisplayColumnWrapper(ruleBuilder.Rule.PropertyName));
        return ruleBuilder;
    }

    public class DisplayColumnWrapper : NoopPropertyValidator, IAttributeMetadataValidator {
        private string name;

        public DisplayColumnWrapper(string name) {
            this.name = name;
        }

        public override IEnumerable<ValidationFailure> Validate(PropertyValidatorContext context) {
            return Enumerable.Empty<ValidationFailure>();
        }

        public Attribute ToAttribute() {
            return new DisplayColumnAttribute(name);
        }
    }
}

... Что вы можете затем использовать следующим образом:

public class Validator : AbstractValidator<SomeModel> {
    public Validator() {
        RuleFor(x => x.DisplayColumnProperty)
            .DisplayColumn();

    }
}

Затем вам нужно будет создать пользовательский ModelMetadataProvider, который знает, как обрабатывать это:

public class ExtendedFVModelMetadataProvider : FluentValidationModelMetadataProvider {
    IValidatorFactory _validatorFactory;

    public ExtendedFVModelMetadataProvider(IValidatorFactory validatorFactory)
        : base(validatorFactory) {
        this._validatorFactory = validatorFactory;
    }

    public override ModelMetadata GetMetadataForType(Func<object> modelAccessor, Type modelType) {
        var validator = _validatorFactory.GetValidator(modelType);

        if (validator == null) {
            return base.GetMetadataForType(modelAccessor, modelType);
        }

        // Only look for the DisplayColumnWrapper 
        // There is a mismatch as MVC expects this to be defined at class-level, but FV defines everything at the property level.
        var displayColumns = from memberWithValidator in validator.CreateDescriptor().GetMembersWithValidators()
                             from propertyValidator in memberWithValidator
                             let wrapper = propertyValidator as MetadataExt.DisplayColumnWrapper
                             where wrapper != null
                             select wrapper.ToAttribute();

        var displayColumn = displayColumns.FirstOrDefault();

        // we found a displaycolumn, so pass it over to MVC to build the metadata.
        if (displayColumn != null) {
            return CreateMetadata(new[] { displayColumn }, null /* containerType */, modelAccessor, modelType, null /* propertyName */);
        }

        return base.GetMetadataForType(modelAccessor, modelType);

    }
}

Поставщик переопределяет метод GetMetadataForModel и ищет любые свойства, которые используют DisplayColumn.Вероятно, это может быть расширено, если вы хотите поддерживать также любые другие пользовательские расширения метаданных.Затем вы можете использовать этого поставщика вместо поставщика метаданных, поставляемого с FluentValidation.

Однако я бы порекомендовал этот подход ... библиотека предназначена для выполнения валидации, а не для генерации метаданных пользовательского интерфейса.

...