Adding DataAnnotation to class when using FluentValidation

时间秒杀一切 提交于 2019-12-01 08:03:06

问题


I use the FluentValidation framework to add validation and annotations to my models in an MVC project.

I need to add data annotations to the class level of a model. Namely, the model needs to have the DisplayColumn attribute added. But, since I use FluentValidation (and have the application's ModelMetadataProvider set to use FluentValidation), even if I put the DisplayColumn attribute on the model class, it isn't used. However, I can't find a way to add that annotation by using FluentValidation.

Does anyone have any idea how I can get that to work?

Thanks


回答1:


I wouldn't actually recommend using the FluentValidationModelMetadataProvider - this was only really ever an experimental addition (which very well may be removed from the next release), and it doesn't support any of the class-level DataAnnotations (such as DisplayColumn). I would suggest that you use FluentValidation only for validation, but stick with attributes for metadata.

That being said, if you really want to get this working then you could implement by using a custom no-op validator that's used only for metadata:

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);
        }
    }
}

... Which you could then use like this:

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

    }
}

You'd then need to create a custom ModelMetadataProvider that knows how to process this:

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);

    }
}

The provider overrides the GetMetadataForModel method and looks for any properties that use DisplayColumn. This could probably be extended if you want to support any other custom metadata extensions too. You could then use this provider in place of the metadata provider that comes with FluentValidation.

However, I still wouldn't recommend this approach...the library is designed for performing validation, not for generating UI metadata.



来源:https://stackoverflow.com/questions/5810851/adding-dataannotation-to-class-when-using-fluentvalidation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!