MVVM / новый объект / обязательный атрибут / проверка перед отправкой - PullRequest
0 голосов
/ 08 сентября 2010

Я использую атрибут Required через класс метаданных служб RIA WCF, а Entity Framework под ним.

Я создаю новую сущность и позволяю виду связываться с моделью представления. Пользователь некоторое время смотрит на него, щелкает по нему и пытается сохранить его.

Сценарий здесь заключается в том, что пользователь не вкладывал и не щелкал поля с обязательным атрибутом поля.

Как мне обеспечить, чтобы все обязательные поля имели данные до их отправки?

Winforms имеет ту же проблему, и я использую для циклического перебора всех полей в форме, чтобы убедиться, что все валидаторы прошли.

Должен ли я переписать этот базовый класс для страницы - СНОВА?

Не существует ли способа обеспечить запуск всех проверок атрибутов перед их отправкой обратно на сервер? Нужно ли использовать отражение и собирать все поля с обязательным атрибутом?

Я использую Entity Framework и исследовал INotifyDataErrorInfo - но он используется после поездки в БД (насколько я понимаю).

Я знаю, что я не первый, кто столкнулся с этим - но в своем исследовании я не могу найти хороший пример этого сценария.

Ответы [ 2 ]

1 голос
/ 09 сентября 2010

Вы смотрели на Validator.ValidateObject в System.ComponentModel.DataAnnotations? Я уверен, что это именно то, что вы просите.

0 голосов
/ 08 сентября 2010

INotifyDataErrorInfo не имеет ничего общего с поездками в БД (или вызовами через границу службы, что, как я полагаю, вы имели в виду).INotifyDataErrorInfo - это интерфейс, который вы можете реализовать в модели представления, чтобы сообщить вашему представлению, что ваша модель представления имеет ошибки проверки.Завершение проверки вашей модели представления для использования этого интерфейса по-прежнему остается за вами, если только это не то, что службы WCF RIA предоставляют вам бесплатно, в чем я не уверен.

Я использую атрибут [Требуется]на мой взгляд, модели только для того, чтобы дать подсказку пользовательскому интерфейсу, что мои поля обязательны для заполнения.Я также реализую INotifyDataErrorInfo в моей модели представления и обязательно вызываю мой метод проверки всякий раз, когда изменяются какие-либо свойства в модели представления.Я также вручную вызываю мой метод проверки, когда пользователь выполняет команду сохранения.

В моем случае я использую библиотеку Fluent Validation для реализации моей логики проверки.Я также создал новый базовый класс для любых моделей представлений, которые нуждаются в логике проверки.

public class ValidatingViewModelBase<T> : ViewModelBase, IValidatingViewModel, INotifyDataErrorInfo
{
    private readonly IValidator<T> _validator;
    private readonly Dictionary<string, List<ValidationInfo>> _errors;

    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

    public ValidatingViewModelBase() : this(null, null)
    {
    }

    public ValidatingViewModelBase(IValidator<T> validator) : this(validator, null)
    {
    }

    public ValidatingViewModelBase(IValidator<T> validator, IMessenger messenger) : base(messenger)
    {
        _validator = validator;
        _errors = new Dictionary<string, List<ValidationInfo>>();
    }

    public IEnumerable GetErrors(string propertyName)
    {
        if (string.IsNullOrEmpty(propertyName))
            return _errors.Values;

        CreateValidationErrorInfoListForProperty(propertyName);
        return _errors[propertyName];
    }

    public bool HasErrors
    {
        get { return _errors.Count > 0; }
    }

    protected virtual void AddValidationErrorForProperty(string propertyName, ValidationInfo validationInfo)
    {
        CreateValidationErrorInfoListForProperty(propertyName);

        if (!_errors[propertyName].Contains(validationInfo))
        {
            _errors[propertyName].Add(validationInfo);
            RaiseErrorsChanged(propertyName);
        }
    }

    protected virtual void ClearValidationErrorsForProperty(string propertyName)
    {
        CreateValidationErrorInfoListForProperty(propertyName);

        if (_errors[propertyName].Count > 0)
        {
            _errors[propertyName].Clear();
            RaiseErrorsChanged(propertyName);
        }
    }

    protected virtual void ClearAllValidationErrors()
    {
        foreach (var propertyName in _errors.Keys)
            ClearValidationErrorsForProperty(propertyName);
        _errors.Clear();
    }

    private void CreateValidationErrorInfoListForProperty(string propertyName)
    {
        if (!_errors.ContainsKey(propertyName))
            _errors[propertyName] = new List<ValidationInfo>();
    }

    protected void RaiseErrorsChanged(string propertyName)
    {
        var handler = ErrorsChanged;
        if (handler != null)
        {
            handler.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
        }
    }

    protected override void RaisePropertyChanged(string propertyName)
    {
        Validate();
        base.RaisePropertyChanged(propertyName);
    }

    public bool Validate()
    {
        if (_validator == null)
            return true;

        if (this is ILoadAndSaveData && !((ILoadAndSaveData)this).HasLoadedData)
            return true;

        ClearAllValidationErrors();

        var results = _validator.Validate(this);

        if (!results.IsValid)
        {
            foreach (var failure in results.Errors)
            {
                AddValidationErrorForProperty(failure.PropertyName, 
                    new ValidationInfo(failure.ErrorMessage, ValidationType.Error));
            }
        }

        return results.IsValid;
    }

    public void SendValidationMessage()
    {
        var message = _errors.Values.SelectMany(propertyValidations => propertyValidations)
            .Aggregate("Please correct validation errors before saving.\r\n",
                (current, validationInfo) => current + ("\r\n· " + validationInfo.Message));

        MessengerInstance.Send(new ErrorMessage(new ErrorInfo { Message = message, Type = "Validation Error" }));
    }

    public bool ValidateAndSendValidationMessage()
    {
        var isValid = Validate();
        if (!isValid)
        {
            SendValidationMessage();
        }
        return isValid;
    }
}

public interface IValidatingViewModel
{
    bool Validate();
    void SendValidationMessage();
    bool ValidateAndSendValidationMessage();
}

public enum ValidationType { Error, Warning }

public class ValidationInfo
{
    public string Message { get; set; }
    public ValidationType Type { get; set; }

    public ValidationInfo(string message, ValidationType validationType)
    {
        Message = message;
        Type = validationType;
    }

    public override string ToString()
    {
        var result = Message;

        if (Type == ValidationType.Warning)
            result = "Warning: " + result;

        return result;
    }
}

Так что мои модели представлений наследуют от этого нового базового класса.создайте класс валидатора для каждой модели представления.

public class ExampleValidator : AbstractValidator<IExampleViewModel>
{
    public TripInformationValidator()
    {
        // validation logic here
    }
}

Надеюсь, это поможет!

...