Как правильно использовать объект NHibernate ISession - Сессия закрыта! ошибки
У меня проблемы с ISessions в NHibernate. Я продолжаю получать "Сессия закрыта!" ошибки. Кто-нибудь может показать мне правильный шаблон, включая определение следующих методов и когда использовать каждый из них:
ISession.Close()
ISession.Dispose()
ISession.Disconnect()
Вот моя проблема. У меня есть настройка обратного вызова для запуска процесса, который присваивает значки игрокам каждые пару минут. Однако я продолжаю получать "Сессия закрыта!" ошибки или ошибки о невозможности связать коллекции.
Вот мой репозиторий:
public class NHibernateRepository : IRepository
{
#region Fields
private ISession _session;
private readonly ISessionFactory _sessionFactory;
#endregion
#region Constructors
public NHibernateRepository(ISessionFactory sessionFactory)
{
_sessionFactory = sessionFactory;
}
#endregion
#region IRepository Implementation
public ISession OpenSession()
{
_session = _sessionFactory.OpenSession();
return _session;
}
public IQueryable<TModel> All<TModel>()
{
return _session.Linq<TModel>();
}
public void Save<TModel>(TModel model)
{
_session.Save(model);
}
public void Update<TModel>(TModel model)
{
_session.Update(model);
}
public void Delete<TModel>(TModel model)
{
_session.Delete(model);
}
public ITransaction BeginTransaction()
{
return _session.BeginTransaction();
}
public void Flush()
{
_session.Flush();
}
#endregion
}
Вот мое использование. Репозиторий вводится через Структурную Карту
private Object _awardBadgesLock = new object(); //In case the callback happens again before the previous one completes
public void AwardBadges()
{
lock (_awardBadgesLock)
{
using(session = _repository.OpenSession())
{
foreach (var user in _repository.All<User>().ToList())
{
var userPuzzles = _repository.All<Puzzle>().ByUser(user.Id).ToList();
var userVotes = _repository.All<Vote>().Where(x => x.UserId == user.Id).ToList();
var userSolutions = _repository.All<Solution>().ByUser(user.Id).ToList().Where(x => !userPuzzles.Select(y => y.Id).Contains(x.PuzzleId));
var ledPuzzles = GetPuzzlesLedByUser(user.Id);
AwardPlayerBadge(user, userSolutions);
AwardCriticBadge(user, userVotes);
AwardCreatorBadge(user, userPuzzles);
AwardRidlerBadge(user, userPuzzles);
AwardSupporterBadge(user, userVotes);
AwardPopularBadge(user, userPuzzles);
AwardNotableBadge(user, userPuzzles);
AwardFamousBadge(user, userPuzzles);
AwardLeaderBadge(user, ledPuzzles);
using (var tx = _repository.BeginTransaction())
{
_repository.Update(user);
tx.Commit();
}
}
}
}
}
4 ответа
Проблема заключается в том, что ISession не является потокобезопасным. Было несколько методов, запущенных в отдельных потоках, которые все создали экземпляр ISession. Проблема была в том, что все они использовали один и тот же SessionFactory. Изображение оба эти метода запускаются в отдельных потоках:
ISessionFactory _sessionFactory;
void MethodOne()
{
using(ISession session = _sessionFactory.OpenSession())
{
//Do something with really quick with the session
//Then dispose of it
}
}
void MethodTwo()
{
//OpenSession() actually returns the same instance used in the
//previous method that has already disposed of the object;
using(ISession session = _sessionFactory.OpenSession())
{
//Do something with a session that has already been disposed
//throws errors
}
}
Как я это исправил, в этих сценариях я отказался от NHIbernate и вместо этого назвал хранимые процессы. Я думаю, что в любом случае это оказалось более продуктивным в моей ситуации.
Вы должны всегда использовать session.Dispose(); Другой для очень странных происшествий
Я советую вам прочитать документацию ISession по https://nhibernate.svn.sourceforge.net/svnroot/nhibernate/trunk/nhibernate/src/NHibernate/ISession.cs
В любом случае, правильный способ очистки после завершения сеанса - это избавиться от него (или, лучше, окружить использование оператором using). В этом случае "использование" закрывает сеанс и подавляет финализатор, то есть предотвращает ненужное переживание объекта сеанса следующего сбора мусора и экономит память.
Если соединение уже закрыто, его удаление не вызовет исключения. С другой стороны, закрытие после удаления (или после закрытия) создает исключение.
Документация рекомендует вызывать разъединение вместо закрытия, потому что это освобождает соединение с пулом соединений. Вы должны вызвать Reconnect перед использованием отключенного сеанса.
Для своих нужд я всегда использую "using", который вызывает Dispose, и никогда не использовал обе эти функции.
Что касается проблемы, ваш метод блокировки верен, пока вы удаляете сеанс, но, вероятно, ошибка лежит в другой части вашего кода. Кстати, о дизайне лучше передать переменную сеанса в репозитории из-за единичной реализации сеанса и агрегировать транзакцию root следующим образом:
using (ISession session = SessionFactory.OpenSession())
{
Repository1 rep1 = new Repository1(session);
Repository2 rep1 = new Repository2(session);
Repository3 rep1 = new Repository3(session);
// some logics
using (var tx = session.BeginTransaction())
tx.Commit();
}
,,,