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")

我还有一个DateRangeModelBinder将两个表单输入绑定到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";
    }
}

我无法让它连接到两个输入。由于拆分输入,几乎就像需要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,它可以将这个极其简单的验证场景呈现为几行代码来实现(这种简单的验证场景应该是这样完成的)。我强烈推荐你这个图书馆。我在所有项目中都使用它,因为我厌倦了 DataAnnotations 进行验证。它们非常有限。

于 2011-12-20T17:52:59.847 回答