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
}

ViewModel のプロパティで DataAnnotations を使用して [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 属性を 2 つの別々の目的 (メタデータと検証) に使用します。

MVC アプリケーションで FluentValidation を有効にすると、FluentValidation は検証インフラストラクチャにフックしますが、メタデータにはフックしません。MVC は引き続きメタデータの属性を使用します。検証だけでなくメタデータにも FluentValidation を使用する場合は、バリデーター クラスに問い合わせる方法を知っている MVC の ModelMetadataProvider のカスタム実装を記述する必要があります。これは、FluentValidation がすぐにサポートするものではありません。

于 2011-10-12T20:33:47.550 に答える
3

デフォルトの DataAnnotations を拡張するカスタム ModelMetadataProvider があり、次のようになります。

  1. DisplayAttribute で何も指定されていない場合、Camel Case から文字列を分割する propertyname から "DisplayName" を取り込みます。
  2. ModelMetadata.IsRequired が false に設定されている場合、(NotNull または NotEmpty 型の) Fluent バリデーター ルールが存在するかどうかがチェックされます。

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 に答える