Должен ли мой репозиторий обеспечивать достоверность данных, если да, то где и как? - PullRequest
3 голосов
/ 31 марта 2009

У меня есть шаблон доступа к хранилищу данных:

IRepository<T>
{
  .. query members ..
  void Add(T item);
  void Remove(T item);
  void SaveChanges();
}

Представьте себе сценарий, в котором у меня есть хранилище пользователей, пользователи имеют уникальное имя пользователя, если я создаю нового пользователя с существующим именем пользователя (представьте, что у меня есть тупой слой пользовательского интерфейса, который не проверяется), когда Я добавляю его в репозиторий, все в порядке ... когда я нажимаю SaveChanges, мой репозиторий пытается сохранить элемент в базе данных, моя база данных, к счастью, применяет эти правила и выдает мне отмененное исключение из-за нарушения уникального ключа.

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

Разве мой репозиторий не должен применять эти правила? что произойдет, если мой носитель будет тупым, например, плоская база данных без каких-либо проверок целостности?

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

Я играл с шаблоном Can, например. CanAdd с Add, add вызовет CanAdd и выдаст недопустимое исключение операции, если can возвращает нарушение. CanAdd также возвращает список нарушений о том, что пошло не так ... таким образом я могу начать укладывать эти подпрограммы через стек ... например, вышеприведенный сервисный уровень также будет иметь метод "Can", который будет возвращать отчет о репозиториях + любые дополнительные нарушения, которые он хотел проверить (такие как более сложные бизнес-правила, например, какие пользователи могут вызывать определенные действия).

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


Отредактируйте, дополнительно в этом случае, как вы обрабатываете проверку сущностей, которые находятся в репозитории и обновляются через отслеживание изменений .. например:

using (var repo = ...)
{
  var user = repo.GetUser('user_b');
  user.Username = 'user_a';

  repo.SaveChanges(); // boom!
}

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

using (var repo = ...)
{
  var newUser = new User('user_c');
  repo.Add(newUser); // so far so good.

  var otherUser = repo.GetUser('user_b');
  otherUser.Username = 'user_c';

  repo.SaveChanges(); // boom!
}

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

Это все еще не останавливает предыдущую проблему с отслеживанием изменений ... так что теперь я должен начать проверять вызов сохранения изменений? похоже, что было бы огромное количество нарушений, которые могли бы произойти из-за явно не связанных действий.

Возможно, я прошу нереальную, совершенную сеть безопасности?

Заранее спасибо, Стивен.

1 Ответ

2 голосов
/ 31 марта 2009

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

К сожалению, проверка на каждом слое также сложна. Мое решение: либо поместите валидацию в одном месте (возможно, на бизнес-уровне), а остальные уровни сделайте действительно тупыми. Они больше ничего не проверяют.

Или запишите свою проверку абстрактным способом в модель, а затем сгенерируйте всю проверку из этого. Например:

String name;
Description nameDesc = new Description("name",
        new MaxLength(20), new NotNull());

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

[РЕДАКТИРОВАТЬ] Для проверки у вас есть только следующие случаи:

  • Дубликат ключа
  • Выше определенного предела
  • Ниже определенного предела
  • Ноль (не указано)
  • Empty
  • Ошибка форматирования (поля даты и т. Д.)

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

Что касается вашего другого вопроса, это ... э-э ... "решено" с помощью протокола двухфазной фиксации . Я говорю «решено», потому что бывают ситуации, когда протокол выходит из строя и, по моему опыту, гораздо лучше дать пользователю «Повторить?» диалоговое окно или другие способы решения проблемы, вместо того, чтобы тратить много времени на TPC.

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