Vlad
Vlad

Reputation: 3181

NullReference when disposing session with transaction

Usually it works fine but in some conditions (I can't reproduce it) I receive NullReferenceException with stacktrace:


   at Npgsql.NpgsqlCommand.ClearPoolAndCreateException(Exception e) in C:\projects\Npgsql2\src\Npgsql\NpgsqlCommand.cs:line 1505
   at Npgsql.NpgsqlCommand.GetReader(CommandBehavior cb) in C:\projects\Npgsql2\src\Npgsql\NpgsqlCommand.cs:line 650
   at Npgsql.NpgsqlCommand.ExecuteBlind() in C:\projects\Npgsql2\src\Npgsql\NpgsqlCommand.cs:line 499
   at Npgsql.NpgsqlTransaction.Rollback() in C:\projects\Npgsql2\src\Npgsql\NpgsqlTransaction.cs:line 185
   at Npgsql.NpgsqlTransaction.Dispose(Boolean disposing) in C:\projects\Npgsql2\src\Npgsql\NpgsqlTransaction.cs:line 141
   at NHibernate.Transaction.AdoTransaction.Dispose(Boolean isDisposing) in p:\nhibernate-core\src\NHibernate\Transaction\AdoTransaction.cs:line 368
   at NHibernate.Impl.SessionImpl.Close() in p:\nhibernate-core\src\NHibernate\Impl\SessionImpl.cs:line 380
   at NHibernate.Impl.SessionImpl.Dispose(Boolean isDisposing) in p:\nhibernate-core\src\NHibernate\Impl\SessionImpl.cs:line 1738
   at NHibernate.Impl.SessionImpl.Dispose() in p:\nhibernate-core\src\NHibernate\Impl\SessionImpl.cs:line 1709
    public virtual IEnumerable<User.PublishedInfo> GetUsersByXP(int count)
    {
        using (ISession session = SessionFactory.OpenSession())
        {
            using (session.BeginTransaction())
            {
                var result = session.CreateCriteria<User>()
                    .SetProjection(PublishedUserProjections)
                    .AddOrder(Order.Desc("XP"))
                    .SetMaxResults(count)
                    .SetResultTransformer(Transformers.AliasToBean<User.PublishedInfo>())
                    .List<User.PublishedInfo>();

                foreach (var r in result)
                    r.Initialize();

                session.Transaction.Commit();

                return result; // this line
            }
        }
    }

What can be possible wrong here?

UPDATE

Sometimes in the same code fragment (but in BeginTransaction) I receive an exception "Timeout while getting a connection". Perhaps this is somehow related.

Upvotes: 1

Views: 1563

Answers (2)

jishi
jishi

Reputation: 24634

I'm seeing the same thing in 2.0.14.3. Which version are you running? And which version of NHibernate?

Looking through the code, I see that

    internal NpgsqlException ClearPoolAndCreateException(Exception e)
    {
        Connection.ClearPool();
        return new NpgsqlException(resman.GetString("Exception_ConnectionBroken"), e);
    }

2.2 seems to suffer from the same

Where as most other operations always make a null check on Connection property. This might be a bug, but it also seem to be rewritten heavily in the master branch.

The only source that I can find that would actually set the connection to null internally (unless it is somehow done by NHibernate but I doubt it) would be if the NpgsqlCommand would be disposed and then read from the NpgsqlDataReader.

EDIT:

According to the source, the Rollback will be invoked on Dispose if the NpgsqlTransaction still has a connection and a transaction active. In your case, the Commit should have set it as null. The fact that you are using session.Transaction in your using block (opposed to storing the reference returned from your BeginTransaction() call) might be the culprit, since a call to session.Transaction implicitly creates a transaction if none is present in your session. This could cause the commit call to actually just commit on an empty transaction, and then the Dispose would be invoked on the transaction that was created with BeginTransaction.

Do you have any multi-threading or asynchronous code that might be working with your session while this code is running?

Upvotes: 1

Low Flying Pelican
Low Flying Pelican

Reputation: 6054

Sounds to me like, if some error occurred inside the transaction, it would throw an exception and it would dispose the session/transaction. Try...

    using (ISession session = SessionFactory.OpenSession())
    {
        List<User.PublishedInfo> results = null;
        ITransaction transaction = null;
        try
        {
            transaction = session.BeginTransaction();
            results = session.CreateCriteria<User>()
                .SetProjection(PublishedUserProjections)
                .AddOrder(Order.Desc("XP"))
                .SetMaxResults(count)
                .SetResultTransformer(Transformers.AliasToBean<User.PublishedInfo>())
                .List<User.PublishedInfo>();

            foreach (var r in results)
                r.Initialize();

            transaction.Commit();
        }
        catch(Exceptionn ex)
        {
            if (transaction != null)
                transaction.Rollback();
            throw;
        }
        return results;
    }

Upvotes: 0

Related Questions