Nick Williams
Nick Williams

Reputation: 1267

NHibernate GenericRepo Memory Leak

I've been using a generic Repository along with NHibernate, but I am having a problem in stopping memory leaks/leaving sessions open.

The method is

public IQueryable<TEntity> Find()
{
   ISession session = _sessionFactory.OpenSession();
   return session.Query<TEntity>();
}

Obviously this leaves the session open, however when I use the follwing

public IQueryable<TEntity> Find()
{
   using (ISession session = _sessionFactory.OpenSession())
   {
       return session.Query<TEntity>();
   }
}

The session is closed when the query is run.

Is there anyway I can dispose of the session after the query has run??

The calling of this method looks like this:

MyRepo repo = new MyRepo();
var list = repo.MyEntity.Find().Where(e => e.Id ==0).First();
//Need to dispose here????

Can this be done without the calling method needing to explicity dispose. I.e. I would like to avoid.

MyRepo repo = new MyRepo();
var list = repo.MyEntity.Find().Where(e => e.Id ==0).First();
repo.DisposeSession();

Thanks in advance.

EDIT

Here is the repo class

public NHibernateRepo<TEntity> : IRepo<TEntity> where TEntity : class
{
     private readonly SessionFactory _sessionFactory;
     public NHibernateRepo(SessionFactory sessionFactory)
     {
       _sessionFactory = sessionFactory;
     }

     public IQueryable<TEntity> Find()
     {
        ISession session = _sessionFactory.OpenSession();
        return session.Query<TEntity>();
     }

     public void Add(TEntity entity)
     {
         using (ISession session = _sessionFactory.OpenSession())
         {
             session.Save(entity);
         }
     }

      //Update and delete methods essentially same as add
}

This session management seems to achieve what I need, however I am sure there must be something unsafe about it. Any thoughts??

public class NHibernateRepo<TEntity> : IRepo<TEntity> where TEntity : class
{
    private readonly ISessionFactory _sessionFactory;
    private ISession _session;

    public NHibernateRepo(Configuration configuration)
    {
        configuration.SetProperty("current_session_context_class", "thread_static");
        _sessionFactory = configuration.BuildSessionFactory();
    }

    private ISession GetOpenSession()
    {
        if (_session == null)
        {
            if (!CurrentSessionContext.HasBind(_sessionFactory))
                CurrentSessionContext.Bind(_sessionFactory.OpenSession());

            _session = _sessionFactory.GetCurrentSession();
        }

        if (!_session.IsOpen)
        {
            _session = _sessionFactory.OpenSession();
        }

        return _session;
    }

    public IQueryable<TEntity> Find()
    {
        ISession session = GetOpenSession();
        session.Clear();

        return session.Query<TEntity>();
    } 

    public void Update(TEntity value)
    {
        using (ISession session = GetOpenSession())
        {
            session.Transaction.Begin();
            session.Update(value);
            session.Transaction.Commit();

            session.Flush();
            session.Clear();
        }
    }

Upvotes: 0

Views: 2046

Answers (1)

tchrikch
tchrikch

Reputation: 2468

What you need to do is to add proper session management to your code , nHibernate can't guess when you're done and dispose your session. A standard way would be to embedd your actions with using statement

public void SomeMethod()
{
     using(var session = _sessionFactory.OpenSession(_session))
     {
         var repository = new NHibernateRepo<MyType>();
         var list = repository.MyEntity.Find().Where(e => e.Id ==0).First();
     } //here everything is disposed
}

Of course in standard way you would wrap the session object inside something else , usually UnitOfWork or similar but the point is that you need to handle session and :

  • open it once when request starts
  • handle it during request
  • dispose at the end
  • dispose if there is any exception

So a real code with exception handling will rather look like

public void SomeMethod()
{
     using(var repository = new NHibernateRepo<MyType>(_sessionFactory))
     {
         var list = repository .MyEntity.Find().Where(e => e.Id ==0).First();
     }
}

public class NHibernateRepo<TEntity> : IDisposable{
    private ISession _session;


   public NHibernateRepo(ISessionFactory sessionFactory) { 
          _session = sessionFactory.OpenSession();
    }

   public IQueryable<TEntity> Find(){

        try{
            return session.Query<TEntity>();
        }
        catch(Exception ex){
             //session clear / close 
             // transaction rollbacks etc.
        }
   }

   public void Dispose(){
     //here goes session close stuff etc.
   }
}

Upvotes: 1

Related Questions