Проверка свойства модели полукомплексного представления в ASP.NET MVC 3 - PullRequest
3 голосов
/ 20 декабря 2011

Я изо всех сил пытаюсь завершить решение проверки сервера-клиента для полусложного сценария. У меня есть тип ядра под названием 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 из-за разделения входов. Есть идеи? Дайте мне знать, если понадобятся дополнительные разъяснения.

1 Ответ

3 голосов
/ 20 декабря 2011

Вы точно не показали, как выглядит ваша пользовательская реализация 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 для проверки. Они настолько ограничены.

...