Добавление DataAnnotation в класс при использовании FluentValidation

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

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

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

Спасибо


person Brian McCord    schedule 27.04.2011    source источник
comment
Даже спустя год может быть полезен этот поставщик метаданных по соглашению: haacked.com/archive/2011/07/14/   -  person Luciano    schedule 25.04.2012


Ответы (1)


На самом деле я бы не рекомендовал использовать 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.

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

person Jeremy Skinner    schedule 28.04.2011
comment
Спасибо, Джереми. Я думал, что это будет что-то вроде этого. Я использовал это средство в FluentValidation, потому что я использую инфраструктуру сущностей и мне не очень нравится загрязнение класса приятелей для чего-то столь же простого в атрибутах. Знаете ли вы, есть ли другой метод, который позволил бы мне использовать FluentValidation только для проверки, но при этом избежать классов друзей? - person Brian McCord; 28.04.2011
comment
Подход класса приятелей действительно довольно ужасен. Единственный другой поставщик метаданных, о котором я знаю, — это «Fluent MetadataProvider», упомянутый в asp-net-mvc.aspx" rel="nofollow noreferrer">weblogs.asp.net/rashid/archive/2009/12/24/, но, к сожалению, код больше не доступен. Я могу подумать о реализации лучшего поставщика метаданных в будущей версии FluentValidation, но на данный момент я сосредоточен только на стороне проверки. - person Jeremy Skinner; 28.04.2011