6

我为标签创建了一个 HtmlHelper,如果需要关联字段,则在该标签的名称后放置一个星号:

public static MvcHtmlString LabelForR<TModel, TValue>(
        this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
    return LabelHelper(
        html,
        ModelMetadata.FromLambdaExpression(expression, html.ViewData),
        ExpressionHelper.GetExpressionText(expression),
        null);
}

private static MvcHtmlString LabelHelper(HtmlHelper helper, ModelMetadata metadata, string htmlFieldName, string text)
{
    ... //check metadata.IsRequired here
    ... // if Required show the star
}

如果我使用 DataAnnotations 并在我的 ViewModel 中的属性上拍 [Required],我的私有 LabelHelper 中的 metadata.IsRequired 将等于 True,并且一切都会按预期工作。

但是,如果我使用 FluentValidation 3.1 并添加这样的简单规则:

public class CheckEmailViewModelValidator : AbstractValidator<CheckEmailViewModel>
{
    public CheckEmailViewModelValidator()
    {
        RuleFor(m => m.Email)
            .NotNull()
            .EmailAddress();
    }
}

...在我的 LabelHelper 元数据中。IsRequired 将被错误地设置为 false。(验证器虽然有效:您不能提交空字段,它需要是电子邮件之类的)。
其余元数据看起来正确(例如:metadata.DisplayName = "Email")。
从理论上讲,如果使用 Rule .NotNull(),FluentValidator 会在属性上添加RequiredAttribute。

供参考:我的 ViewModel:

[Validator(typeof(CheckEmailViewModelValidator))]
public class CheckEmailViewModel
{
    //[Required]
    [Display(Name = "Email")]
    public string Email { get; set; }
}

我的控制器:

public class MemberController : Controller
{
    [HttpGet]
    public ActionResult CheckEmail()
    {
        var model = new CheckEmailViewModel();
        return View(model);
    }
}

任何帮助表示赞赏。

4

2 回答 2

4

默认情况下,MVC 将 DataAnnotations 属性用于两个不同的目的 - 元数据和验证。

当您在 MVC 应用程序中启用 FluentValidation 时,FluentValidation 会挂钩到验证基础结构而不是元数据 - MVC 将继续使用元数据的属性。如果您想将 FluentValidation 用于元数据和验证,那么您需要编写 MVC 的 ModelMetadataProvider 的自定义实现,该实现知道如何询问验证器类 - 这不是 FluentValidation 开箱即用的支持。

于 2011-10-12T20:33:47.550 回答
3

我有一个自定义的 ModelMetadataProvider,它增强了默认的 DataAnnotations 一个,提供以下内容:

  1. 如果没有通过 DisplayAttribute 指定,则从来自 Camel Case 的属性名拆分字符串填充“DisplayName”。
  2. 如果 ModelMetadata.IsRequired 设置为 false,它会检查是否存在任何流畅的验证器规则(类型为 NotNull 或 NotEmpty)。

我确实检查了 Jeremy 准备的源代码,但我还没有准备好进行全面检修,所以我混合和匹配,以免失去默认行为。你可以在这里找到

这是从这篇文章中获取的一些额外优点的代码。

public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
    readonly IValidatorFactory factory;
    public CustomModelMetadataProvider(IValidatorFactory factory) 
        : base() {
        this.factory = factory;
    }

    // Uppercase followed by lowercase but not on existing word boundary (eg. the start) 
    Regex _camelCaseRegex = new Regex(@"\B\p{Lu}\p{Ll}", RegexOptions.Compiled);
    // Creates a nice DisplayName from the model’s property name if one hasn't been specified 

    protected override ModelMetadata GetMetadataForProperty(
        Func<object> modelAccessor, 
        Type containerType,
        PropertyDescriptor propertyDescriptor) {

        ModelMetadata metadata = base.GetMetadataForProperty(modelAccessor, containerType, propertyDescriptor);
        metadata.IsRequired = metadata.IsRequired || IsNotEmpty(containerType, propertyDescriptor.Name);
        if (metadata.DisplayName == null)
            metadata.DisplayName = displayNameFromCamelCase(metadata.GetDisplayName());

        if (string.IsNullOrWhiteSpace(metadata.DisplayFormatString) && 
            (propertyDescriptor.PropertyType == typeof(DateTime) || propertyDescriptor.PropertyType == typeof(DateTime?))) {
            metadata.DisplayFormatString = "{0:d}";
        }

        return metadata;
    }

    string displayNameFromCamelCase(string name) {
        name = _camelCaseRegex.Replace(name, " $0");
        if (name.EndsWith(" Id"))
            name = name.Substring(0, name.Length - 3);
        return name;
    }

    bool IsNotEmpty(Type type, string name) {
        bool notEmpty = false;
        var validator = factory.GetValidator(type);

        if (validator == null)
            return false;

        IEnumerable<IPropertyValidator> validators = validator.CreateDescriptor().GetValidatorsForMember(name);

        notEmpty = validators.OfType<INotNullValidator>().Cast<IPropertyValidator>()
                             .Concat(validators.OfType<INotEmptyValidator>().Cast<IPropertyValidator>()).Count() > 0;
        return notEmpty;
    }
}
于 2013-02-20T18:11:32.440 回答