Валидатор объекта - это хороший дизайн? - PullRequest
1 голос
/ 25 декабря 2010

Я работаю над проектом, в котором методы API, которые я пишу, должны возвращать различные "представления" объектов домена, например:

namespace View.Product
{
    public class SearchResult : View
    {
        public string Name { get; set; }
        public decimal Price { get; set; }
    }

    public class Profile : View
    {
        public string Name { get; set; }
        public decimal Price { get; set; }

        [UseValidationRuleset("FreeText")]
        public string Description { get; set; }

        [SuppressValidation]
        public string Comment { get; set; }
    }
}

Это также аргументы методов установки в API, которые должны быть проверены перед сохранением их в БД. Я написал валидатор объекта, который позволяет пользователю определять наборы правил валидации в файле XML и проверяет, соответствует ли объект этим правилам:

[Validatable]
public class View
{
    [SuppressValidation]
    public ValidationError[] ValidationErrors
    {
        get { return Validator.Validate(this); }
    }
}

public static class Validator
{
    private static Dictionary<string, Ruleset> Rulesets;

    static Validator()
    {
        // read rulesets from xml
    }

    public static ValidationError[] Validate(object obj)
    {
        // check if obj is decorated with ValidatableAttribute
        // if not, return an empty array (successful validation)

        // iterate over the properties of obj
        // - if the property is decorated with SuppressValidationAttribute, 
        //   continue
        // - if it is decorated with UseValidationRulesetAttribute, 
        //   use the ruleset specified to call 
        //   Validate(object value, string rulesetName, string FieldName)
        // - otherwise, get the name of the property using reflection and
        //   use that as the ruleset name
    }

    private static List<ValidationError> Validate(object obj, string fieldName, string rulesetName)
    {
        // check if the ruleset exists, if not, throw exception
        // call the ruleset's Validate method and return the results
    }
}

public class Ruleset
{
    public Type Type { get; set; }
    public Rule[] Rules { get; set; }

    public List<ValidationError> Validate(object property, string propertyName)
    {
        // check if property is of type Type
        // if not, throw exception

        // iterate over the Rules and call their Validate methods
        // return a list of their return values
    }
}

public abstract class Rule
{
    public Type Type { get; protected set; }
    public abstract ValidationError Validate(object value, string propertyName);
}

public class StringRegexRule : Rule
{
    public string Regex { get; set; }

    public StringRegexRule()
    {
        Type = typeof(string);
    }

    public override ValidationError Validate(object value, string propertyName)
    {
        // see if Regex matches value and return
        // null or a ValidationError
    }
}

Фу ... Спасибо, что прочитали все это. Я уже реализовал это, и он прекрасно работает, и я планирую расширить его, чтобы проверить содержимое полей IEnumerable и других полей, которые Validatable.

  • Что меня особенно беспокоит, так это то, что если набор правил не указан, валидатор пытается использовать имя свойства в качестве имени набора правил. (Если вам не нужно такое поведение, вы можете использовать [SuppressValidation].) Это делает код намного менее загроможденным (не нужно использовать [UseValidationRuleset("something")] для каждого отдельного свойства), но это почему-то не кажется правильным. Я не могу решить, ужасно это или круто. Что ты думаешь?
  • Любые предложения по другим частям этого дизайна тоже приветствуются. Я не очень опытный, и я благодарен за любую помощь.
  • Кроме того, "Validatable" - это хорошее имя? Для меня это звучит довольно странно, но я не являюсь носителем английского языка.

1 Ответ

1 голос
/ 25 декабря 2010

Мое предложение использует интерфейс вместо атрибутов:

public interface IValidatable
{
    ValidationError[] Validate(Rulesets ruleSets);
}

public class View : IValidatable
{
    public ValidationError[] Validate(Rulesets ruleSets)
    {
       // do validate
    }
}

public static class Validator
{
    private static Rulesets _rulesets;

    static Validator()
    {
        // read rulesets
    }

    public static ValidationError[] Validate(object obj)
    {
        IValidatable validObj = obj as IValidatable;
        if (obj == null)
            // not validatable
            return new ValidationError[0];

        return validObj.Validate(_rulesets);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...