3

やや複雑なシナリオでサーバーとクライアントの検証ソリューションを完成させるのに苦労しています。と呼ばれるコアタイプがありDateRangeます:

public class DateRange {
    public DateRange (DateTime? start, DateTime? end) { ... }
    public DateTime? Start { get; private set; }
    public DateTime? End { get; private set; }
}

私は次のようなビューモデルを持っています:

public class MyViewModel {
    public DateRange Period { get; set; }
}

次のような %mvcproject%\Views\Shared\EditorTemplates\DateRange.cshtml があります。

@model MyCore.DateRange

@Html.Editor("Start", "Date")
@Html.Editor("End", "Date")

DateRangeModelBinder2 つのフォーム入力をDateRangeプロパティにバインドする必要もあります。私が抱えている問題は次のDateRangeRequiredAttributeとおりです。

public class DateRangeRequired : ValidationAttribute, IClientValidatable,
    IMetadataAware
{
    private const string DefaultErrorMessage =
           "{0} is required.";

    public DateRangeRequired(bool endIsRequired = true)
        : base(() => DefaultErrorMessage)
    {
        EndIsRequired = endIsRequired;
    }

    public bool EndIsRequired { get; set; }

    public override bool IsValid(object value)
    {
        if (value == null)
        {
            return false;
        }
        if (!value.GetType().IsAssignableFrom(typeof(DateRange)))
        {
            throw new ArgumentException("Value is not a DateRange.");
        }
        var dateRange = value as DateRange;
        return (dateRange.Start.HasValue && !EndIsRequired) ||
               (dateRange.Start.HasValue && dateRange.End.HasValue && EndIsRequired);
    }

    public override string FormatErrorMessage(string name)
    {
        return string.Format(CultureInfo.CurrentCulture, ErrorMessageString, name);
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule()
        {
            ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
            ValidationType = "daterangerequired"
        };
        rule.ValidationParameters.Add("endisrequired", EndIsRequired.ToString().ToLower());
        yield return rule;
    }

    public void OnMetadataCreated(ModelMetadata metadata)
    {
        metadata.DataTypeName = "DateRange";
    }
}

2つの入力に接続できません。ValidatorTemplate入力が分割されているため、 EditorTemplate とペアになる が必要なようです。何か案は?追加の説明が必要な場合はお知らせください。

4

1 に答える 1

3

カスタム実装がどのようDateRangeRequiredAttributeに見えるかを正確に示していないので、例を提案させてください:

public class DateRangeRequiredAttribute : ValidationAttribute, IClientValidatable
{
    private readonly string _otherProperty;
    public DateRangeRequiredAttribute(string otherProperty)
    {
        _otherProperty = otherProperty;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var property = validationContext.ObjectType.GetProperty(_otherProperty);
        if (property == null)
        {
            return new ValidationResult(string.Format(CultureInfo.CurrentCulture, "Unknown property {0}", _otherProperty));
        }
        var otherValue = property.GetValue(validationContext.ObjectInstance, null);
        if (!(value is DateTime) || !(otherValue is DateTime))
        {
            return new ValidationResult(string.Format(CultureInfo.CurrentCulture, "The two properties to compare must be of type DateTime"));
        }

        if ((DateTime)value >= (DateTime)otherValue)
        {
            return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
        }
        return null;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule
        {
            ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
            ValidationType = "daterange"
        };
        rule.ValidationParameters.Add("other", "*." + _otherProperty);
        yield return rule;
    }
}

次に、ビューモデルをそれで飾ることができます:

public class DateRange
{
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:d}")]
    [DateRangeRequired("End", ErrorMessage = "Please select a start date before the end date")]
    public DateTime? Start { get; set; }

    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:d}")]
    [Required]
    public DateTime? End { get; set; }
}

最後に、ビューでアダプターを登録します。

jQuery.validator.unobtrusive.adapters.add(
    'daterange', ['other'], function (options) {
        var getModelPrefix = function (fieldName) {
            return fieldName.substr(0, fieldName.lastIndexOf(".") + 1);
        };
        var appendModelPrefix = function (value, prefix) {
            if (value.indexOf('*.') === 0) {
                value = value.replace('*.', prefix);
            }
            return value;
        };
        var prefix = getModelPrefix(options.element.name),
        other = options.params.other,
        fullOtherName = appendModelPrefix(other, prefix),
        element = $(options.form).find(':input[name="' + fullOtherName + '"]')[0];

        options.rules['daterange'] = element;
        if (options.message) {
            options.messages['daterange'] = options.message;
        }
    }
);

jQuery.validator.addMethod('daterange', function (value, element, params) {
    // TODO: some more advanced date checking could be applied here
    // currently it uses the current browser culture setting to perform
    // the parsing. If you needed to use the server side culture, this code
    // could be adapted respectively

    var date = new Date(value);
    var otherDate = new Date($(params).val());
    return date < otherDate;
}, '');

このポルノグラフィーを読んだ後、FluentValidation.NETの使用を検討するかもしれません。FluentValidation.NETは、この非常に単純な検証シナリオを数行で実装します (これは、このような単純な検証シナリオを実行する方法です)。このライブラリを強くお勧めします。検証用の DataAnnotations にうんざりしているため、すべてのプロジェクトで使用しています。それらは非常に限られています。

于 2011-12-20T17:52:59.847 に答える