Рекомендации по проверке POCO с помощью ASP.NET MVC/Entity Framework

Здесь сценарий:

  • Веб-приложение ASP.NET MVC2
  • Entity Framework 4 (чистый POCO, настраиваемый контекст данных)
  • Шаблон хранилища
  • Единица измерения работы
  • Инъекция зависимостей
  • Сервисный уровень посредника Контроллер → Репозиторий

Итак, в основном, все классные вещи.:)

Поток событий для основной операции пользовательского интерфейса ( "Добавление сообщения" ):

  • Контроллер вызывает метод Добавить (сообщение) на уровне службы
  • Звонки уровня обслуживания Добавить (T) в репозитории
  • Репозиторий вызывает AddObject (T) в контексте пользовательских данных
  • Контроллер вызывает Commit() на блоке работы

Теперь я пытаюсь выяснить, где я могу поместить мою проверку.

На этом этапе мне нужны два типа проверки:

  • Простая независимая проверка POCO, такая как "почта должна иметь заголовок". Это кажется естественным подспорьем для аннотаций данных в POCO.
  • Сложная проверка бизнеса, такая как "нельзя добавлять комментарий к заблокированному сообщению". Это невозможно сделать с помощью аннотаций данных.

Теперь, я читал "Программирование Entity Framework, второе издание" Джули Лерман (что отличное BTW), и я искал привязку к SavingChanges, чтобы выполнить проверку "последней минуты". Это было бы хорошим способом убедиться, что проверка всегда происходит всякий раз, когда я делаю "что-то" (добавление, изменение, удаление), но также немного поздняя IMO (поскольку элементы уже находятся в диспетчере состояний) - так что я могу сделать, если проверка не удалась, удалите их?

Я мог бы, конечно, сделать мой POCO реализовать интерфейс (скажем, "IValidatable" ) и вызвать метод на этом интерфейсе во время этого события.

Но это кажется "слишком поздно" для проверки бизнеса - это консенсус?

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

Другой кривый шар для вас - как вы знаете, POCO с EF означает, что POCO имеет все свойства в БД - поэтому у меня может быть свойство "PostID", с помощью get/set accessors (поскольку EF необходимо получить/установить эти свойства).

Но проблема в том, что "PostID" - столбец identity, так как я могу защитить поле от того, чтобы он был установлен в явном виде? Например, если я (по какой-либо причине) делает следующее:

var post = service.FindSingle(10);
post.PostId = 10;
unitOfWork.Commit();

Это вызовет исключение SqlException. Как я могу предотвратить это? Я не могу "скрыть" свойство (сделать его приватным или даже внутренним), поскольку POCO находятся в отдельной сборке в репозитории.

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

Таким образом, я могу закодировать что-то вроде этого на моем контроллере:

[HttpPost]
public ActionResult AddPost(Post post)
{
   try
   {
      IUnitOfWork uow = new UnitOfWork();
      postService.Add(post);
      uow.Commit();
   }
   catch(InvalidPostOperation ipo)
   {
      // add error to viewmodel
   }
}

Нужно ли вручную выполнять проверку на уровне сервиса каждый раз, когда я делаю Add? Тогда как я могу справиться с сохранением? (так как это находится в Единице работы, а не в сервисном слое).

Итак, чтобы это не было вопросом "повсюду", вот мои вопросы:

  • Простая проверка POCO - следует ли это делать с аннотациями данных? Плюсы/минусы/подводные камни?
  • При каких обстоятельствах (если есть) мы должны подключаться к событию SavingChanges контекста данных EF, чтобы обеспечить проверку?
  • Где я должен выполнять комплексную проверку бизнеса? В сервисе explicity или в методе POCO (который я могу позвонить из службы). Как создать интеллектуальную/многоразовую схему?
  • Как мы можем "скрыть" автоматически сгенерированные свойства POCO от подделки?

Любые мысли будут оценены наиболее высоко.

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

Спасибо.

ИЗМЕНИТЬ

Ниже приведен полезный ответ, но я все еще (идеально) искал больше мыслей. Кто-нибудь еще?

Ответы

Ответ 1

  • Хорошо, как вы сказали, DataAnnotations не подходит для всех ситуаций. Минусы - это, в основном, сложная валидация (множественное свойство и несколько объектов с разными свойствами).
  • Если бы я был вами, я бы оставил работу по проверке бизнеса/домена из уровня данных (EF) максимально возможной. Если есть сценарий проверки уровня данных, то тонкий (например, проверка сложных родительских/дочерних отношений - это чисто материал базы данных).
  • Да, сложная проверка бизнеса должна выполняться на уровне обслуживания или в объектах модели (с помощью частичных классов или какого-либо метода наследования: интерфейсы/производные классы). Там дебаты об этом между людьми ActiveRecord, людьми с шаблонами репозитория и людьми с DDD, но идут с тем, что работает для вас, просто и позволят быстро развертывать и поддерживать недорогие приложения. Это простой пример того, как вы могли бы добавить более сложную проверку объектов домена, но все же совместим с интерфейсом DataAnnotations и, следовательно, является "MVC friendly".
  • Хороший вопрос. -не я не нашел решение, на 100% доволен. Я играл с идеей частных сеттеров, и это не здорово. Быстро прочитайте эту обобщенную книгу Evans DDD. Он очень быстро читается, и это может дать некоторое представление о цели и различии между объектами модели и объектами Value. Именно здесь я думаю, что дизайн объекта будет смягчать проблемы, связанные с тем, что у вас есть свойство "подделывать" (как вы его называете), но не фиксируя видимость свойства. То есть другое решение могло бы быть в другом месте. Надеюсь, это поможет.

Ответ 2

Эй, наверное, немного поздно, но здесь все равно...

Все зависит от вашей архитектуры, т.е. существует ли логическое разделение в вашем приложении: пользовательский интерфейс, уровень обслуживания, уровень репозитория. Если вы подключаетесь к событию "Сохранить", как именно это будет сделано? Из того, что я заметил, вы бы назвали репозиторий Layer for Persistance только правильно? Однако вы подключаетесь к событию сохранения, предоставляя управление обратно на уровень обслуживания/бизнес-уровень, что бы вы не заставляли сохранить его правильно?

Я лично считаю, что уровень сервиса/бизнес-уровень должен позаботиться об этом в завершении, а затем скажите, эй mr repo layer → сохранить этот объект.

Что касается проверки, аннотации данных следует использовать с пользовательским интерфейсом, поэтому простая валидация, такая как [Обязательный] и т.д., это будет полезно при проверке на стороне клиента, но сложная бизнес-логика или сложная проверка должны быть подключены к уровню обслуживания/бизнес-уровень, таким образом, он повторно используется для всех объектов/объектов/POCOS и т.д.

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

var updatedpost = _repo.GetPost(post.postid);
updatedpost.comment = post.comment;
updatedpost.timestamp = datetime.now;

Вид расточительного, но таким образом ваш уровень buseinss берет под контроль, однако это только мой опыт, я могу ошибаться, я много читал в привязке к модели, validaiton и других материалах, однако, похоже, были случаи, когда вещи никогда не работают ожидается, например [Обязательный] атрибут (см. Статью Брэда Уилсона).