Вопрос

Я пытаюсь освоить NHibernate, Fluent NHibernate и Spring.

Следуя принципам предметно-ориентированного проектирования, я пишу стандартное многоуровневое веб-приложение, состоящее из:

  • уровень представления (ASP.Net)
  • бизнес-уровень, включающий:
    • уровень приложения (по сути, набор методов, видимых для уровня пользовательского интерфейса)
    • интерфейсы репозитория и компоненты домена (используемые уровнем приложений)
  • Уровень персистентности (по сути, реализация интерфейсов репозитория, определенных на бизнес-уровне).

Мне бы хотелось помочь определить способ создания экземпляра NHibernate ISession таким образом, чтобы он мог использоваться несколькими репозиториями в течение всего времени существования одного запроса к бизнес-уровне.В частности, я хотел бы:

  • разрешить управление экземпляром ISession и любой транзакцией вне реализации репозитория (возможно, с помощью какого-то аспекта структуры IOC, перехватчика?)

  • разрешить доступность экземпляра ISession для репозиториев удобным для тестирования способом (возможно, посредством внедрения или через некоторую общую абстракцию «контекста»)

  • избегать создания ненужных транзакций (т.когда выполнялись только операции только для чтения)

  • позвольте мне писать тесты, использующие SQLLite

  • позвольте мне использовать Fluent NHibernate

  • позволить реализации репозитория оставаться в неведении о среде хоста.Я пока не знаю, будет ли бизнес-уровень работать вместе с уровнем представления или будет размещаться отдельно в WCF (в IIS), поэтому я не хочу слишком тесно привязывать свой код к контексту HTTP (например, ).

Моей первой попыткой решить эту проблему было использование шаблона реестра;сохранение экземпляра ISession в свойстве ThreadStatic.Однако последующее чтение показало, что это не лучшее решение (поскольку ASP.Net может переключать поток в течение жизненного цикла страницы, я считаю).

Любые мысли, решения по деталям, названия шаблонов, указатели на актуальные образцы (NHibernate 2) будут приняты с благодарностью.

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

Решение

Я не использовал Spring.NET, поэтому не могу это комментировать.Однако остальное звучит замечательно (а может, и не так уж замечательно;мы едва ли первые, кто реализовал эти вещи;) аналогично моему собственному опыту.У меня тоже были проблемы с поиском одной истинно лучшей практики, поэтому я просто прочитал столько, сколько мог, и придумал свою собственную интерпретацию.

В моей ситуации я хотел, чтобы управление транзакциями/сессиями было внешним по отношению к репозиторию, а также чтобы проблемы репозитория не выходили из него (т.коду, использующему репозиторий, не нужно знать, что он использует NHibernate внутри, и ему не нужно ничего знать об управлении сеансами NHibernate).В моем случае было решено, что транзакции будут создаваться по умолчанию, чтобы разработчики не забыли о них, поэтому мне пришлось иметь механизм выхода только для чтения.Я выбрал шаблон Unit of Work с хранилищем экземпляров NHibernate ISession внутри.Код вызова (я также создал интерфейс DSL для UoW) может выглядеть примерно так:

using (var uow = UoW.Start().ReadOnly().WithHttpContext()
       .InNewScope().WithScopeContext(ScopeContextProvider.For<CRMModel>())
{
    // Repository access
}

На практике это может быть всего лишь UoW.Start() в зависимости от того, сколько контекста уже доступно.А HttpContext часть относится к месту хранения UoW, которым, что неудивительно, является HttpContext в этом случае.Как вы упомянули, для приложения ASP .NET HttpContext самое безопасное место для хранения вещей. ScopeContextProvider в основном гарантирует, что для UoW предоставлен правильный контекст данных (экземпляр ISession для соответствующей базы данных/сервера, другие настройки).Концепция «ScopeContext» также упрощает вставку «тестового» контекста области.

Если пойти по этому пути, репозитории будут явно зависеть от интерфейса UoW.На самом деле, возможно, вам удастся немного абстрагировать это, но я не уверен, что вижу в этом выгоду.Я имею в виду, что каждый метод репозитория извлекает текущий экземпляр UoW, а затем извлекает объект ISession (или просто SqlConnection для тех методов, которые не используют NHibernate) для запуска запроса/операции NHibernate.Однако это работает для меня, потому что это также кажется идеальным временем, чтобы убедиться, что текущий UoW не доступен только для чтения для методов, которым может потребоваться запуск CRUD.

В целом, я думаю, что это один из подходов, который решает все ваши вопросы:

  • Позволяет управлять сеансами вне репозитория.
  • Контекст ISession можно имитировать или указывать на поставщика контекста для тестовой среды.
  • Избегает ненужных транзакций (ну, вам придется инвертировать то, что я сделал, и иметь .Transactional() позвони или типа того)
  • Я не понимаю, почему вы не можете протестировать с помощью SQLite, поскольку это скорее проблема NHibernate.
  • Я сам использую Fluent NHibernate
  • Позволяет репозиторию игнорировать среду хоста (т. е. вызывающая сторона репозитория контролирует контекст хранилища UoW)

Что касается реализации UoW, я частично корю себя за то, что не осмотрелся по сторонам перед тем, как начать.Есть проект под названием машина.уоу который, как я понимаю, довольно популярен и хорошо работает с NHibernate.Я мало с ним играл, поэтому не могу сказать, решает ли он все мои требования так же четко, как тот, который я написал сам, но это также могло бы сэкономить время на разработку.

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

Для справки, стек программного обеспечения, который я использую:

  • АСП.NET MVC
  • Свободное владение NHibernate поверх NHibernate
  • Ninject для внедрения зависимостей

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

То, что вы описываете, поддерживается инфраструктурой Spring.NET практически из коробки.Только для FluentNHibernate нужно добавить собственный SessionFactory (кода не много, посмотрите здесь:Использование Fluent NHibernate в Spring.NET) в Spring.NET.

Каждый репозиторий может использовать один и тот же ISession, просто внедрите SessionFactory в свои репозитории и используйте службы транзакций Spring.NET.

Просто попробуйте, у них довольно подробная документация, имхо.

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