Проверка объекта на основе внешних факторов (т.е.уникальность хранилища данных)

StackOverflow https://stackoverflow.com/questions/1721327

Вопрос

Описание

В моем решении есть следующие проекты:

  • DAL = Модифицированная структура сущностей
  • DTO = Объекты передачи данных, способные проверять себя
  • БЛ = Сервисы бизнес-уровня
  • ПАУТИНА = презентация Asp.net Приложение MVC

DAL, BL и WEB ссылаются на DTO, и это здорово.
Процесс обычно выполняется таким образом:

  1. Веб-запрос отправляется в ИНТЕРНЕТ
  2. В ИНТЕРНЕТЕ публикуются DTO
    • DTO автоматически проверяются с помощью пользовательского ActionFilter
    • ошибки проверки собираются автоматически
  3. (Проверка в порядке) ВЕБ-вызовы в BL, предоставляющие DTO
  4. BL вызывает DAL с помощью DTO (может либо передавать их, либо просто использовать)

Тогда проблема с проверкой DTO...

Мои DTO могут проверять себя на основе своего собственного состояния (значений свойств).Но прямо сейчас я столкнулся с проблемой, когда это не так.Мне нужно, чтобы они проверялись с помощью BL (и, следовательно, DAL).

Мой пример из реальной жизни:Пользователь регистрируется, и WEB получает пользовательский DTO, который проходит проверку.Проблемная часть заключается в следующем username проверка подлинности.Его уникальность должна быть проверена на соответствие хранилищу данных.
Как я должен это сделать?

Есть дополнительная информация о том, что все DTO реализуют интерфейс (т.е. User Инструменты DTO IUser) для целей МоК и TDD.И то, и другое является частью Проект DTO.

Невозможные попытки

  1. Я не могу ссылаться на BL в DTO, потому что я получу циклическую ссылку.
    Compilation error
  2. Я не могу создать дополнительный проект DTO.Val, который ссылался бы на частичные классы DTO и реализовывал бы там их проверку (они ссылались бы на BL + DTO).
    Partial classes can't span assemblies.

Возможные попытки

  1. Создайте особый ActionFilter это позволило бы проверить объект на соответствие внешним условиям.Этот был бы создан внутри ВЕБ-проект таким образом, видя DTO и BL, которые будут использоваться здесь.
  2. Поместите DTO в BL и сохраните интерфейсы DTO как фактические DTO, на которые ссылаются другие проекты, и реорганизуйте весь код, чтобы использовать интерфейсы вместо конкретных классов.
  3. Не обрабатывайте внешнюю зависимую проверку и позвольте внешним зависимостям выдавать исключение - вероятно самое худшее решение этой проблемы

Что бы вы предложили?

Это было полезно?

Решение 4

Результирующее решение

В итоге я использовал фильтр действий контроллера, который смог проверить объект на соответствие внешним факторам, которые не могут быть получены из самого объекта.

Я создал фильтр, который принимает имя параметра действия для проверки и тип валидатора, который будет проверять этот конкретный параметр.Конечно, этот валидатор должен реализовать определенный интерфейс, чтобы сделать все это многоразовым.

[ValidateExternalFactors("user", typeof(UserExternalValidator))]
public ActionResult Create(User user)

валидатор должен реализовать этот простой интерфейс

public interface IExternalValidator<T>
{
    bool IsValid(T instance);
}

Это простое и эффективное решение, казалось бы, сложной проблемы.

Другие советы

Я бы предложил эксперимент, который я опробовал только на прошлой неделе или около того.

Основанный на это вдохновение я создаю DTO, которые проверяются немного иначе, чем у DataAnnotations подходите.Образец DTO:

public class Contact : DomainBase, IModelObject
{
    public int ID { get; set; }
    public string Name { get; set; }
    public LazyList<ContactDetail> Details { get; set; }
    public DateTime Updated { get; set; }


    protected override void ConfigureRules()
    {
        base.AddRule(new ValidationRule()
        {
            Properties = new string[] { "name" },
            Description = "A Name is required but must not exceed 300 characters in length and some special characters are not allowed",
            validator = () => this.Name.IsRequired300LenNoSpecial()
        });

        base.AddRule(new ValidationRule()
        {
            Properties = new string[] { "updated" },
            Description = "required",
            validator = () => this.Updated.IsRequired()
        });
    }
}

Это может показаться более трудоемким, чем DataAnnotations и что ж, это так, потому что это так, но это не так уж и важно.Я думаю, что это более презентабельно в классе (сейчас у меня есть несколько действительно уродливых классов DTO с DataAnnotations атрибуты - вы даже больше не можете видеть свойства).И сила анонимных делегатов в этом приложении почти достойна книги (так что я открываю для себя).

Базовый класс:

public partial class DomainBase : IDataErrorInfo
{
    private IList<ValidationRule> _rules = new List<ValidationRule>();

    public DomainBase()
    {
        // populate the _rules collection
        this.ConfigureRules();
    }

    protected virtual void ConfigureRules()
    {
        // no rules if not overridden
    }

    protected void AddRule(ValidationRule rule)
    {
        this._rules.Add(rule);
    }





    #region IDataErrorInfo Members

    public string Error
    {
        get { return String.Empty; }    // Validation should call the indexer so return "" here
    }                                   // ..we dont need to support this property.

    public string this[string columnName]
    {
        get
        {
            // get all the rules that apply to the property being validated
            var rulesThatApply = this._rules
                .Where(r => r.Properties.Contains(columnName));

            // get a list of error messages from the rules
            StringBuilder errorMessages = new StringBuilder();
            foreach (ValidationRule rule in rulesThatApply)
                if (!rule.validator.Invoke())   // if validator returns false then the rule is broken
                    if (errorMessages.ToString() == String.Empty)
                        errorMessages.Append(rule.Description);
                    else
                        errorMessages.AppendFormat("\r\n{0}", rule.Description);

            return errorMessages.ToString();
        }
    }

    #endregion
}

ValidationRule и мои функции проверки:

public class ValidationRule
{
    public string[] Properties { get; set; }
    public string Description { get; set; }
    public Func<bool> validator { get; set; }
}


/// <summary>
/// These extention methods return true if the validation condition is met.
/// </summary>
public static class ValidationFunctions
{
    #region IsRequired

    public static bool IsRequired(this String str)
    {
        return !str.IsNullOrTrimEmpty();
    }

    public static bool IsRequired(this int num)
    {
        return num != 0;
    }

    public static bool IsRequired(this long num)
    {
        return num != 0;
    }

    public static bool IsRequired(this double num)
    {
        return num != 0;
    }

    public static bool IsRequired(this Decimal num)
    {
        return num != 0;
    }

    public static bool IsRequired(this DateTime date)
    {
        return date != DateTime.MinValue;
    }

    #endregion


    #region String Lengths

    public static bool IsLengthLessThanOrEqual(this String str, int length)
    {
        return str.Length <= length;
    }

    public static bool IsRequiredWithLengthLessThanOrEqual(this String str, int length)
    {
        return !str.IsNullOrTrimEmpty() && (str.Length <= length);
    }

    public static bool IsRequired300LenNoSpecial(this String str)
    {
        return !str.IsNullOrTrimEmpty() &&
            str.RegexMatch(@"^[- \r\n\\\.!:*,@$%&""?\(\)\w']{1,300}$",
                RegexOptions.Multiline) == str;
    }

    #endregion

}

Если мой код выглядит беспорядочно, то это потому, что я работал над этим подходом к проверке только последние несколько дней.Мне нужна эта идея, чтобы соответствовать нескольким требованиям:

  • Мне нужно поддержать IDataErrorInfo интерфейс, поэтому мой уровень MVC проверяется автоматически
  • Мне нужно иметь возможность поддерживать сложные сценарии проверки (я думаю, в этом весь смысл вашего вопроса):Я хочу иметь возможность проверять несколько свойств одного и того же объекта (т.е.Дата начала и дата завершения);свойства из разных / нескольких / связанных объектов, как у меня было бы в графе объектов;и даже о других вещах, о которых я еще не подумал.
  • Мне нужно поддержать идею об ошибке, применимой к нескольким свойствам
  • В рамках моего путешествия по TDD и DDD я хочу, чтобы объекты моего домена больше описывали мой "домен", чем мои методы уровня обслуживания, поэтому включение этих сложных условий в объекты модели (не DTO), по-видимому, позволяет достичь этого

Я думаю, такой подход даст мне то, что я хочу, и, возможно, вам тоже.

Я бы предположил, что если ты согласишься со мной в этом вопросе, мы будем довольно "одиноки", но, возможно, оно того стоит.Я читал о новые возможности проверки в MVC 2 но он по-прежнему не соответствует приведенному выше списку пожеланий без пользовательских изменений.

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

Архитектура S # arp имеет идентификатор метода [DomainSignature], который используется с валидатором уровня класса [HasUniqueDomainSignature] для выполнения этой работы.Смотрите пример кода ниже:

[HasUniqueDomainSignature]
public class User : Entity
{
    public User()
    {
    }

    public User(string login, string email) : this()
    {
        Login = login;
        Email = email;
    }

    [DomainSignature]
    [NotNullNotEmpty]
    public virtual string Login { get; set; }

    [DomainSignature]
    public virtual string Email { get; set; }

}

Взгляните поближе на http://www.sharparchitecture.net/

У меня была точно такая же проблема, и после долгих попыток найти обходной путь в течение нескольких дней я в итоге объединил свои DTO, DAL и BL в одну библиотеку.Я сохранил свой уровень презентации отдельно.Не уверен, подходит ли вам такой вариант или нет.Что касается меня, то я полагал, что мои шансы когда-либо сменить хранилище данных были очень невелики, и поэтому отдельный уровень на самом деле не был нужен.

Я также внедрил блок приложений Microsoft Validation Application для всех моих проверок DTO.У них есть метод "Самоконтроля", который позволяет выполнять сложные проверки.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top