У меня есть рамки настойчивость, построенный на вершине NHibernate, который используется в нескольких веб-приложениях. Он скрывает реализацию NH за интерфейсом IRepository
и IRepository<T>
, с конкретными экземплярами, предоставленными Unity (таким образом, я мог бы теоретически заменить NHibernate, например, на Entity Framework довольно легко).
Поскольку Unity не поддерживает (или, по крайней мере, версию, которую я использую), поддерживает передачу параметров конструктора, отличных от тех, которые являются самими инъекциями зависимостей, передача в существующем NH ISession невозможна; но я хочу, чтобы все объекты в UOW делились тем же ISession.
Я решить эту проблему, имея контрольный хранилище класса, который управляет доступом к ISession на каждой нити основы:
public static ISession Session
{
get
{
lock (_lockObject)
{
// if a cached session exists, we'll use it
if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
{
return (ISession)PersistenceFrameworkContext.Current.Items[NHibernateRepository.SESSION_KEY];
}
else
{
// must create a new session - note we're not caching the new session here... that's the job of
// BeginUnitOfWork().
return _factory.OpenSession(new NHibernateInterceptor());
}
}
}
}
В этом примере, PersistenceFrameworkContext.Current.Items
получает доступ к IList<object>
, который хранится либо ThreadStatic
, если не в Веб-контекст или в пределах HttpContext.Current.Items
, если он находится в веб-контексте (чтобы избежать проблем с пулом потоков). Первый вызов свойства создает экземпляр ISession
из хранимого заводского экземпляра, последующие вызовы просто извлекают его из хранилища. Блокировка немного замедлит работу, но не так сильно, как просто блокировка статического экземпляра ISession
с аппроксимацией.
У меня тогда есть BeginUnitOfWork
и EndUnitOfWork
методы позаботиться о UOW - я специально запретил вложенные UOW, потому что откровенно они были болью для управления.
public void BeginUnitOfWork()
{
lock (_lockObject)
{
if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
EndUnitOfWork();
ISession session = Session;
PersistenceFrameworkContext.Current.Items.Add(SESSION_KEY, session);
}
}
public void EndUnitOfWork()
{
lock (_lockObject)
{
if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
{
ISession session = (ISession)PersistenceFrameworkContext.Current.Items[SESSION_KEY];
PersistenceFrameworkContext.Current.Items.Remove(SESSION_KEY);
session.Flush();
session.Dispose();
}
}
}
Наконец, пара методов обеспечения доступа к хранилищам домена типа конкретных: (. Здесь PersistentObject<T>
является базовым классом, обеспечивающим идентификатор и Равно поддержки)
public IRepository<T> For<T>()
where T : PersistentObject<T>
{
return Container.Resolve<IRepository<T>>();
}
public TRepository For<T, TRepository>()
where T : PersistentObject<T>
where TRepository : IRepository<T>
{
return Container.Resolve<TRepository>();
}
Таким образом, доступ к данному хранилищу находится в шаблоне
NHibernateRepository.For<MyDomainType>().Save();
Это тогда facaded над таким образом, что вы можете использовать
MyDomainType.Repository.Save();
Если данный тип имеет специализированное хранилище (то есть нужно больше, чем он может получить от IRepository<T>
), то я создать интерфейс, производный от IRepository<T>
, расширяющаяся реализации наследуемых от моего IRepository<T>
реализации и в самом типе домена я переопределить статический Repository
свойства, используя new
new public static IUserRepository Repository
{
get
{
return MyApplication.Repository.For<User, IUserRepository>();
}
}
(MyApplication
[который называется что-то меньше Нодди в реальном продукте] класс фасад, который заботится о supplyi нг экземпляра Repository
с помощью Unity, так что вы не имеете зависимости от конкретной реализации хранилища NHibernate в классах домена.)
Это дает мне полную pluggability с помощью Unity для реализации хранилища, легкий доступ к хранилищу в коде без прыжков через обручи , и прозрачный, для потоковой обработки ISession
.
Существует намного больше кода, чем просто то, что выше (и я упростил код примера), но вы получаете общую идею.
MyApplication.Repository.BeginUnitOfWork();
User user = User.Repository.FindByEmail("[email protected]");
user.FirstName = "Joe"; // change something
user.LastName = "Bloggs";
// you *can* call User.Repository.Save(user), but you don't need to, because...
MyApplication.Repository.EndUnitOfWork();
// ...causes session flush which saves the changes automatically
В моем веб-приложение, у меня есть сеанс-за-запроса, так и BeginUnitOfWork
EndUnitOfWork
дозвонились в BeginRequest
и EndRequest
соответственно.
Это, как вы говорите, технически ответ на мой вопрос, даже если это не лучшее решение в архитектуре. Я зеленаю тебя. – David