Skip to content

Intermittent null reference exception on CloseConnection #2336

Closed
@kriewall

Description

@kriewall

Describe the bug

Intermittent NullReferenceException thrown at NHibernate.Connection.ConnectionProvider.CloseConnection(DbConnection conn)

Steps to reproduce

I unfortunately don't have a simple standalone project to reproduce this issue. I see it from time to time when running regression tests.

Expected behavior

  • No null reference exception should occur.

Environment

  • Windows 10
  • NHibernate 5.2.7
  • Oracle.ManagedDataAccess 18.6.0

AdditionalContext

Full stack trace below, together with my (condensed) code for UnitOfWork - credits to Vladimir Khorikov - and SessionFactory construction. Mine is a multi-threaded application. The SessionFactory is created one and used across the app. Multiple sessions may be opened simultaneously using the same connection string, but session usage always occurs within a single unit of work, and a new unit of work is always newed up for a given command (i.e. I don't believe the same unit of work can be used across multiple threads). If I've misunderstood the documentation regarding session usage in a multithreaded environment, please help me understand my error.

Message: System.Exception : Exception=Object reference not set to an instance of an object.
StackTrace=
   at NHibernate.Connection.ConnectionProvider.CloseConnection(DbConnection conn)
   at NHibernate.Connection.DriverConnectionProvider.CloseConnection(DbConnection conn)
   at NHibernate.Transaction.AdoNetTransactionFactory.ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork work, Boolean transacted)
   at NHibernate.Transaction.AdoNetWithSystemTransactionFactory.ExecuteWorkInIsolation(ISessionImplementor session, IIsolatedWork work, Boolean transacted)
   at NHibernate.Engine.Transaction.Isolater.DoIsolatedWork(IIsolatedWork work, ISessionImplementor session)
   at NHibernate.Engine.TransactionHelper.DoWorkInNewTransaction(ISessionImplementor session)
   at NHibernate.Id.TableGenerator.Generate(ISessionImplementor session, Object obj)
   at NHibernate.Id.TableHiLoGenerator.Generate(ISessionImplementor session, Object obj)
   at NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId(Object entity, String entityName, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
   at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event)
   at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event)
   at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event)
   at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event)
   at NHibernate.Impl.SessionImpl.FireSaveOrUpdate(SaveOrUpdateEvent event)
   at NHibernate.Impl.SessionImpl.SaveOrUpdate(String entityName, Object obj)
   at NHibernate.Engine.CascadingAction.SaveUpdateCascadingAction.Cascade(IEventSource session, Object child, String entityName, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeToOne(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeAssociation(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeProperty(Object parent, Object child, IType type, CascadeStyle style, String propertyName, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeCollectionElements(Object parent, Object child, CollectionType collectionType, CascadeStyle style, IType elemType, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeCollection(Object parent, Object child, CascadeStyle style, Object anything, CollectionType type)
   at NHibernate.Engine.Cascade.CascadeAssociation(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeProperty(Object parent, Object child, IType type, CascadeStyle style, String propertyName, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeOn(IEntityPersister persister, Object parent, Object anything)
   at NHibernate.Event.Default.AbstractSaveEventListener.CascadeAfterSave(IEventSource source, IEntityPersister persister, Object entity, Object anything)
   at NHibernate.Event.Default.AbstractSaveEventListener.PerformSaveOrReplicate(Object entity, EntityKey key, IEntityPersister persister, Boolean useIdentityColumn, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
   at NHibernate.Event.Default.AbstractSaveEventListener.PerformSave(Object entity, Object id, IEntityPersister persister, Boolean useIdentityColumn, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
   at NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId(Object entity, String entityName, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
   at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event)
   at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event)
   at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event)
   at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event)
   at NHibernate.Impl.SessionImpl.FireSaveOrUpdate(SaveOrUpdateEvent event)
   at NHibernate.Impl.SessionImpl.SaveOrUpdate(String entityName, Object obj)
   at NHibernate.Engine.CascadingAction.SaveUpdateCascadingAction.Cascade(IEventSource session, Object child, String entityName, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeToOne(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeAssociation(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeProperty(Object parent, Object child, IType type, CascadeStyle style, String propertyName, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeCollectionElements(Object parent, Object child, CollectionType collectionType, CascadeStyle style, IType elemType, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeCollection(Object parent, Object child, CascadeStyle style, Object anything, CollectionType type)
   at NHibernate.Engine.Cascade.CascadeAssociation(Object parent, Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeProperty(Object parent, Object child, IType type, CascadeStyle style, String propertyName, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeOn(IEntityPersister persister, Object parent, Object anything)
   at NHibernate.Event.Default.AbstractFlushingEventListener.CascadeOnFlush(IEventSource session, IEntityPersister persister, Object key, Object anything)
   at NHibernate.Event.Default.AbstractFlushingEventListener.PrepareEntityFlushes(IEventSource session)
   at NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExecutions(FlushEvent event)
   at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event)
   at NHibernate.Impl.SessionImpl.Flush()
   at NHibernate.Impl.SessionImpl.FlushBeforeTransactionCompletion()
   at NHibernate.Impl.SessionImpl.BeforeTransactionCompletion(ITransaction tx)
   at NHibernate.Transaction.AdoTransaction.Commit()
   at MyApp.Core.Domain.Core.CommandQueryHandling.UnitOfWork.Commit() in C:\src\NSG\MyApp\MyApp.Core\Domain\Core\CommandQueryHandling\UnitOfWork.cs:line 38
   at MyApp.Core.Domain.State.Commands.AdvanceStateHistoryLegacy.AdvanceStateHistoryHandler.<>c__DisplayClass3_1.<Handle>b__6() in C:\src\NSG\MyApp\MyApp.Core\Domain\State\Commands\AdvanceStateHistoryLegacy.cs:line 56
   at CSharpFunctionalExtensions.ResultExtensions.OnSuccess(Result result, Action action)
   at MyApp.Core.Domain.State.Commands.AdvanceStateHistoryLegacy.AdvanceStateHistoryHandler.Handle(AdvanceStateHistoryLegacy command) in C:\src\NSG\MyApp\MyApp.Core\Domain\State\Commands\AdvanceStateHistoryLegacy.cs:line 53
   at CallSite.Target(Closure , CallSite , Object , Object )
   at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
   at CallSite.Target(Closure , CallSite , Object , Object )
   at MyApp.Core.Domain.Core.CommandQueryHandling.Messages.Dispatch(ICommand command) in C:\src\NSG\MyApp\MyApp.Core\Domain\Core\CommandQueryHandling\Messages.cs:line 22
   at MyApp.Core.CrossCutting.Extensions.ResultExtensions.Map[T](Result`1 result, Func`2 func) in C:\src\NSG\MyApp\MyApp.Core\CrossCutting\Extensions\ResultExtensions.cs:line 88
   at MyApp.Core.Application.Runjobs.StoreMeasurements.StoreMeasurementsRunjob.GetNormalReply(StoreMeasurementsContext runjobCollaterals) in C:\src\NSG\MyApp\MyApp.Core\Application\Runjobs\StoreMeasurements\StoreMeasurementsRunjob.cs:line 61
   at MyApp.Core.Application.Runjobs.StoreMeasurements.StoreMeasurementsRunjob.<>c__DisplayClass7_0.<Handle>b__3(IntroType introType) in C:\src\NSG\MyApp\MyApp.Core\Application\Runjobs\StoreMeasurements\StoreMeasurementsRunjob.cs:line 45
   at MyApp.Core.CrossCutting.Extensions.ResultExtensions.Map[T,K](Result`1 result, Func`2 func) in C:\src\NSG\MyApp\MyApp.Core\CrossCutting\Extensions\ResultExtensions.cs:line 82
   at MyApp.Core.Application.Runjobs.StoreMeasurements.StoreMeasurementsRunjob.Handle(XDocument runjob) in C:\src\NSG\MyApp\MyApp.Core\Application\Runjobs\StoreMeasurements\StoreMeasurementsRunjob.cs:line 45
   at MyApp.Core.Application.Runjobs.GenericRunjob.<>c__DisplayClass7_0.<Handle>b__3(IClassifiedRunjob runjob) in C:\src\NSG\MyApp\MyApp.Core\Application\Runjobs\GenericRunjob.cs:line 42
   at MyApp.Core.CrossCutting.Extensions.ResultExtensions.Map[T,K](Result`1 result, Func`2 func) in C:\src\NSG\MyApp\MyApp.Core\CrossCutting\Extensions\ResultExtensions.cs:line 82
   at MyApp.Core.Application.Runjobs.GenericRunjob.Handle(XDocument xDoc) in C:\src\NSG\MyApp\MyApp.Core\Application\Runjobs\GenericRunjob.cs:line 39
   at MyApp.Core.Application.Entry.NonComServiceRequest.<>c__DisplayClass4_1.<HandleRunjob>b__9(IRunjob`1 runjob) in C:\src\NSG\MyApp\MyApp.Core\Application\Entry\NonComServiceRequest.cs:line 40
   at MyApp.Core.CrossCutting.Extensions.ResultExtensions.Map[T,K](Result`1 result, Func`2 func) in C:\src\NSG\MyApp\MyApp.Core\CrossCutting\Extensions\ResultExtensions.cs:line 82
   at MyApp.Core.Application.Entry.NonComServiceRequest.HandleRunjob(String method, String requestXml) in C:\src\NSG\MyApp\MyApp.Core\Application\Entry\NonComServiceRequest.cs:line 37
public sealed class UnitOfWork : IDisposable
    {
        private readonly ISession _session;
        private readonly ITransaction _transaction;
        private bool _isAlive = true;
        private bool _disposed = false;

        public UnitOfWork(SessionFactory sessionFactory)
        {
            _session = sessionFactory.OpenSession();
            _transaction = _session.BeginTransaction(IsolationLevel.ReadCommitted);
        }

        public void Commit()
        {
            if (!_isAlive)
                return;

            try
            {
                _transaction.Commit();
            }
            finally
            {
                _isAlive = false;
                Dispose();
            }
        }
 
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (_disposed) return;
 
            if (disposing)
            {
                _transaction.Dispose();
                _session.Dispose();
            }
 
            _disposed = true;
        }
    }
         private ISessionFactory DoBuildSessionFactory(ConnectionString connectionString)
        {
            _logger.LogWarning("Constructing session factory");
            lock (_lockObject)
            {
                var dbConfig = OracleDataClientConfiguration.Oracle10
                    .ConnectionString(c => c.Is(connectionString))
                    .Driver<NHibernate.Driver.OracleManagedDataClientDriver>();
                        //.ShowSql();
                dbConfig = ApplicationGlobals.DefaultSchema.Equals(ApplicationGlobals.WindowsUser) ? dbConfig : dbConfig.DefaultSchema(ApplicationGlobals.DefaultSchema);

                FluentConfiguration configuration = Fluently.Configure()
                    .Database(dbConfig)
                    .Mappings(m =>
                        {
                            m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()).AddConventions()/*.ExportTo(@"c:\temp\mappings")*/;
                            m.HbmMappings.AddFromAssembly(Assembly.GetExecutingAssembly());
                        })
                    .ExposeConfiguration(x =>
                    {
                        x.EventListeners.PostCommitUpdateEventListeners = new IPostUpdateEventListener[] { new EventListener() };
                        x.EventListeners.PostCommitInsertEventListeners = new IPostInsertEventListener[] { new EventListener() };
                        x.EventListeners.PostCommitDeleteEventListeners = new IPostDeleteEventListener[] { new EventListener() };
                        x.EventListeners.PostCollectionUpdateEventListeners = new IPostCollectionUpdateEventListener[] { new EventListener() };
                        x.DataBaseIntegration(dbi =>
                        {
                            dbi.Batcher<OracleDataClientBatchingBatcherFactory>();
                            dbi.Dialect<Oracle10gDialect>();
                        });
                        //x.SetInterceptor(new SqlStatementInterceptor());
                    });
   

                try
                {
                    var factory = configuration.BuildSessionFactory();
                    return factory;
                }
                catch (FluentConfigurationException e)
                {
                    var sb = new StringBuilder();
                    var fullMessage = e.FullMessage();
                    sb.AppendLine($"Fatal error building sessionFactory: '{fullMessage}'.");
                    sb.AppendLine($"Potential reasons:");
                    foreach (var reason in e.PotentialReasons) sb.AppendLine(reason);
                    sb.AppendLine($"Executing assembly path = {Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)}");
                    _logger.LogCritical(e, $"Fatal error building session factory: {fullMessage}");
                    throw new System.Exception(sb.ToString());
                }
            }

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions