Asp.net mvc 使用FluentValidation时向类添加DataAnnotation

Asp.net mvc 使用FluentValidation时向类添加DataAnnotation,asp.net-mvc,data-annotations,fluentvalidation,Asp.net Mvc,Data Annotations,Fluentvalidation,我使用FluentValidation框架在MVC项目中向模型添加验证和注释 我需要向模型的类级别添加数据注释。也就是说,模型需要添加DisplayColumn属性。但是,由于我使用FluentValidation(并且将应用程序的ModelMetadataProvider设置为使用FluentValidation),即使我将DisplayColumn属性放在模型类上,也不会使用它。但是,我找不到使用FluentValidation添加该注释的方法 有人知道我怎样才能让它工作吗 谢谢我并不推荐使

我使用FluentValidation框架在MVC项目中向模型添加验证和注释

我需要向模型的类级别添加数据注释。也就是说,模型需要添加DisplayColumn属性。但是,由于我使用FluentValidation(并且将应用程序的ModelMetadataProvider设置为使用FluentValidation),即使我将DisplayColumn属性放在模型类上,也不会使用它。但是,我找不到使用FluentValidation添加该注释的方法

有人知道我怎样才能让它工作吗


谢谢

我并不推荐使用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);
        }
    }
}
公共静态类MetadataExt{
公共静态IRuleBuilder选项显示列(此IRuleBuilder规则){
var ruleBuilder=(FluentValidation.Internal.ruleBuilder)规则;
ruleBuilder.Rule.AddValidator(新的DisplayColumnWrapper(ruleBuilder.Rule.PropertyName));
返回规则生成器;
}
公共类DisplayColumnWrapper:NoopPropertyValidator、IAttributeMetadataValidator{
私有字符串名称;
公共DisplayColumnWrapper(字符串名称){
this.name=名称;
}
公共重写IEnumerable验证(PropertyValidatorContext上下文){
返回可枚举的.Empty();
}
属性()的公共属性{
返回新的DisplayColumnAttribute(名称);
}
}
}
。。。您可以这样使用:

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

    }
}
公共类验证程序:AbstractValidator{
公共验证器(){
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);

    }
}
公共类ExtendedFVModelMetadataProvider:FluentValidationModelMetadataProvider{ IValidatorFactory(美国)工厂;; 公共扩展FVModelMetadataProvider(IValidatorFactory验证工厂) :基地(工厂){ 此._validatorFactory=validatorFactory; } 公共重写ModelMetadata GetMetadataForType(Func modelAccessor,类型modelType){ var validator=\u validatorFactory.GetValidator(modelType); if(验证器==null){ 返回base.GetMetadataForType(modelAccessor,modelType); } //只查找DisplayColumnWrapper //这是不匹配的,因为MVC希望在类级别定义,但FV在属性级别定义所有内容。 var displayColumns=来自validator.CreateDescriptor().GetMembersWithValidators()中的memberWithValidator 来自memberWithValidator中的propertyValidator 让wrapper=propertyValidator作为MetadataExt.DisplayColumnWrapper 其中包装器!=null 选择wrapper.ToAttribute(); var displayColumn=displayColumns.FirstOrDefault(); //我们发现了一个displaycolumn,所以将其传递给MVC以构建元数据。 如果(displayColumn!=null){ 返回CreateMatadata(新[]{displayColumn},null/*containerType*/,modelAccessor,modelType,null/*propertyName*/); } 返回base.GetMetadataForType(modelAccessor,modelType); } } 提供程序重写GetMetadataForModel方法并查找使用DisplayColumn的任何属性。如果您还想支持任何其他自定义元数据扩展,则可以对其进行扩展。然后可以使用此提供程序代替FluentValidation附带的元数据提供程序


但是,我仍然不推荐这种方法……该库是为执行验证而设计的,而不是为生成UI元数据而设计的

谢谢,杰里米。我以为会是这样的。我在FluentValidation中使用了这个工具,因为我使用的是实体框架,对于简单的at属性,我并不喜欢buddy类“污染”。你知道有没有另一种方法可以让我只使用FluentValidation进行验证,而不使用buddy类?buddy类方法确实非常可怕。据我所知,唯一的另一个元数据提供程序是在中提到的“Fluent MetadataProvider”,但不幸的是,代码似乎不再可用。我可能会考虑在未来版本的FluentValidation中实现更好的元数据提供程序,但目前我只关注验证方面的事情。即使一年后,按照惯例,此元数据提供程序可能会很有用: