Проверка C #: IDataErrorInfo без жестко закодированных строк имени свойства? - PullRequest
12 голосов
/ 08 декабря 2010

Какова лучшая практика внедрения IDataErrorInfo?Есть ли способ реализовать его без жестко закодированных строк для имени свойства?

Ответы [ 3 ]

13 голосов
/ 08 декабря 2010

Базовый класс для общих процедур проверки

Вы можете использовать DataAnnotations , если вы выполните некоторые операции в реализации IDataErrorInfo.Например, вот модель базового представления, которую я часто использую (из Windows Forms, но вы можете экстраполировать):

public class ViewModelBase : IDataErrorInfo, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public SynchronizationContext Context
    {
        get;
        set;
    }

    public bool HasErrors
    {
        get
        {
            return !string.IsNullOrWhiteSpace(this.Error);
        }
    }

    public string Error
    {
        get 
        {
            var type = this.GetType();
            var modelClassProperties = TypeDescriptor
                .GetProperties(type)
                .Cast();

            return
                (from modelProp in modelClassProperties
                    let error = this[modelProp.Name]
                    where !string.IsNullOrWhiteSpace(error)
                    select error)
                    .Aggregate(new StringBuilder(), (acc, next) => acc.Append(" ").Append(next))
                    .ToString();
        }
    }

    public virtual string this[string columnName]
    {
        get
        {
            var type = this.GetType();
            var modelClassProperties = TypeDescriptor
                .GetProperties(type)
                .Cast();

            var errorText =
                (from modelProp in modelClassProperties
                    where modelProp.Name == columnName
                    from attribute in modelProp.Attributes.OfType()
                    from displayNameAttribute in modelProp.Attributes.OfType()
                    where !attribute.IsValid(modelProp.GetValue(this))
                    select attribute.FormatErrorMessage(displayNameAttribute == null ? modelProp.Name : displayNameAttribute.DisplayName))
                    .FirstOrDefault();

            return errorText;
        }
    }

    protected void NotifyPropertyChanged(string propertyName)
    {
        if (string.IsNullOrWhiteSpace(propertyName))
        {
            throw new ArgumentNullException("propertyName");
        }

        if (!this.GetType().GetProperties().Any(x => x.Name == propertyName))
        {
            throw new ArgumentException(
                "The property name does not exist in this type.",
                "propertyName");
        }

        var handler = this.PropertyChanged;
        if (handler != null)
        {
            if (this.Context != null)
            {
                this.Context.Post(obj => handler(this, new PropertyChangedEventArgs(propertyName)), null);
            }
            else
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

Пример использования:

public class LogOnViewModel : ViewModelBase
{
    [DisplayName("User Name")]
    [Required]
    [MailAddress] // This is a custom DataAnnotation I wrote
    public string UserName
    {
        get
        {
                return this.userName;
        }
        set
        {
                this.userName = value;
                this.NotifyPropertyChanged("UserName");
        }
    }

    [DisplayName("Password")]
    [Required]
    public string Password
    {
        get; // etc
        set; // etc
    }
}

Использование IDataErrorInfoдля одноразовых процедур проверки

Если честно, в итоге я использую как аннотации, так и переключатель.Я использую аннотации для простых проверок, и если у меня есть более сложные проверки (такие как «проверять только это свойство, если установлено это другое свойство»), я прибегну к переключателю в переопределении индекса this[].Этот шаблон часто выглядит следующим образом (просто вымышленный пример, он не имеет смысла:

public override string this[string columnName]
{
    get
    {
        // Let the base implementation run the DataAnnotations validators
        var error = base[columnName];

        // If no error reported, run my custom one-off validations for this
        // view model here
        if (string.IsNullOrWhiteSpace(error))
        {
            switch (columnName)
            {
                case "Password":
                    if (this.Password == "password")
                    {
                        error = "See an administrator before you can log in.";
                    }

                    break;
            }
        }

        return error;
    }

Некоторые мнения

Что касается указания имен свойств в виде строк: вы могли бы сделатьс лямбдами, но мой честный совет: просто преодолеть это . Вы можете заметить, что в моем ViewModelBase мой маленький помощник NotifyPropertyChanged выполняет магию отражения, чтобы убедиться, что у меня нет жира-предоставил имя свойства - это помогает мне быстрее обнаружить ошибку привязки данных, а не бегать в течение 20 минут, чтобы выяснить, что я пропустил.

Ваше приложение получит спектр проверки от piddlyтакие вещи, как «требуется» или «максимальная длина» на уровне свойств пользовательского интерфейса, до «требуется», только если что-то еще проверено »на другом уровне пользовательского интерфейса и вплоть до« имени пользователя не существует »на уровне домена / персистентности.Вы обнаружите, что вам придется искать компромисс между повторением небольшой логики проверки в пользовательском интерфейсе и добавлением большого количества метаданных в домене к дес.напишите сам пользовательский интерфейс, и вам придется найти компромисс в отношении того, как эти различные классы ошибок проверки отображаются для пользователя.

Надеюсь, что это поможет.Удачи!

2 голосов
/ 08 декабря 2010

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

0 голосов
/ 08 декабря 2010

В этой ситуации (и INotifyPropertyChanged) я склоняюсь к использованию частного статического класса, объявляющего все имена свойств константами:

public class Customer : INotifyPropertyChanging, INotifyPropertyChanged, IDataErrorInfo, etc
{
  private static class Properties
  {
    public const string Email = "Email";
    public const string FirstName = "FirstName";
  }  


}

Все еще есть небольшое повторение, но оно отлично работает для меня в нескольких проектах.

Что касается организации проверки ... Вы могли бы рассмотреть отдельный класс CustomerValidator, который будет предоставлен во время выполнения. Затем вы можете поменять местами разные реализации для разных контекстов. Так, например, новые клиенты могут быть проверены не так, как существующие, без каких-либо условий.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...