Это типичная проблема. Используя NHibernate или Fluent-NHibernate, каждый класс, который вы используете, который сопоставляет ваши данные, украшен (поэтому они должны быть виртуальными) с большим количеством материала. Это происходит все во время выполнения.
Ваш код четко показывает открытие и закрытие сеанса в операторе использования. Находясь в режиме отладки, отладчик очень хорош (или нет), чтобы держать сеанс открытым после окончания оператора using (код очистки вызывается после того, как вы перестанете проходить). Находясь в режиме выполнения (без перехода), ваш сеанс корректно закрыт.
Сессия жизненно важна в NH. Когда вы передаете информацию (результирующий набор), сессия должна оставаться открытой. Обычный шаблон программирования с NH - это открыть сеанс в начале запроса и закрыть его в конце (с asp.net) или оставить его открытым в течение более длительного периода.
Чтобы исправить ваш код, либо переместите сеанс открытия / закрытия в одиночный файл, либо в оболочку, которая может позаботиться об этом. Или переместите сеанс открытия / закрытия в вызывающий код (но через некоторое время это становится грязным). Чтобы исправить это в целом, существует несколько шаблонов. Вы можете найти эту статью о лучших практиках NHibernate , которая охватывает все это.
РЕДАКТИРОВАТЬ: Взятый в другую крайность: архитектура S # arp ( download ) заботится об этих лучших практиках и многих других проблемах NH для вас, полностью скрывая сложности NH конечный пользователь / программист. У него немного крутая кривая обучения (включая MVC и т. Д.), Но как только вы освоите его ... вы уже не сможете обойтись. Не уверен, что его легко смешать с FluentNH.
Использование FluentNH и простой оболочки Dao
Смотрите комментарии, почему я добавил эту дополнительную "главу". Вот пример очень простой, но многократно используемой и расширяемой оболочки Dao для ваших классов DAL. Я предполагаю, что вы настроили свою конфигурацию FluentNH и ваши типичные POCO и отношения.
Следующая обертка - это то, что я использую для простых проектов. Он использует некоторые из шаблонов, описанных выше, но, очевидно, не все для простоты. Этот метод также можно использовать с другими ORM, если вам интересно. Идея состоит в том, чтобы создать синглтон для сеанса, но при этом сохранить возможность закрытия сеанса (для экономии ресурсов) и не беспокоиться о необходимости повторного открытия. Я оставил код для закрытия сессии, но это будет всего пара строк. Идея заключается в следующем:
// the thread-safe singleton
public sealed class SessionManager
{
ISession session;
SessionManager()
{
ISessionFactory factory = Setup.CreateSessionFactory();
session = factory.OpenSession();
}
internal ISession GetSession()
{
return session;
}
public static SessionManager Instance
{
get
{
return Nested.instance;
}
}
class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly SessionManager instance = new SessionManager();
}
}
// the generic Dao that works with your POCO's
public class Dao<T>
where T : class
{
ISession m_session = null;
private ISession Session
{
get
{
// lazy init, only create when needed
return m_session ?? (m_session = SessionManager.Instance.GetSession());
}
}
public Dao() { }
// retrieve by Id
public T Get(int Id)
{
return Session.Get<T>(Id);
}
// get all of your POCO type T
public IList<T> GetAll(int[] Ids)
{
return Session.CreateCriteria<T>().
Add(Expression.In("Id", Ids)).
List<T>();
}
// save your POCO changes
public T Save(T entity)
{
using (var tran = Session.BeginTransaction())
{
Session.SaveOrUpdate(entity);
tran.Commit();
Session.Refresh(entity);
return entity;
}
}
public void Delete(T entity)
{
using (var tran = Session.BeginTransaction())
{
Session.Delete(entity);
tran.Commit();
}
}
// if you have caching enabled, but want to ignore it
public IList<T> ListUncached()
{
return Session.CreateCriteria<T>()
.SetCacheMode(CacheMode.Ignore)
.SetCacheable(false)
.List<T>();
}
// etc, like:
public T Renew(T entity);
public T GetByName(T entity, string name);
public T GetByCriteria(T entity, ICriteria criteria);
Тогда в вашем коде вызова это выглядит примерно так:
Dao<Foo> daoFoo = new Dao<Foo>();
Foo newFoo = new Foo();
newFoo.Name = "Johnson";
daoFoo.Save(newFoo); // if no session, it creates it here (lazy init)
// or:
Dao<Bar> barDao = new Dao<Bar>();
List<Bar> allBars = barDao.GetAll();
Довольно просто, не правда ли? Продвижение к этой идее заключается в создании определенных Dao для каждого POCO, которые наследуются от вышеуказанного общего класса Dao, и используют класс доступа для их получения. Это облегчает добавление задач, специфичных для каждого POCO, и это, в основном, и было целью NH Best Practices (в двух словах, потому что я исключил интерфейсы, отношения наследования и статические и динамические таблицы).