public IDatabaseConnectionMonitoringHandle GetMonitoringHandle()
        {
            lock (this.Lock)
            {
                // Since this will be called via non-thread-safe paths, we do a true
                // dispose check. This error should never reach callers unless they are
                // using non-thread-safe stuff concurrently
                if (this._state == State.Disposed)
                {
                    throw new ObjectDisposedException(this.GetType().ToString());
                }

                // If the connection is already closed, we'll never see a state change
                // event for the close so just return an already canceled handle
                if (this._state == State.AutoStopped || this._state == State.Stopped)
                {
                    return(new AlreadyCanceledHandle());
                }

                // If the connection does not support state monitoring, we can't produce
                // a monitoring handle
                if (this._stateChangedHandler == null)
                {
                    return(NullHandle.Instance);
                }

                var hadRegisteredMonitoringHandles = this.HasRegisteredMonitoringHandlesNoLock;

                var connectionLostTokenSource = new CancellationTokenSource();
                var handle = new MonitoringHandle(this, connectionLostTokenSource.Token);
                (this._monitoringHandleRegistrations ??= new Dictionary <MonitoringHandle, CancellationTokenSource>())
                .Add(handle, connectionLostTokenSource);

                if (!this.StartMonitorWorkerIfNeededNoLock() &&
                    !hadRegisteredMonitoringHandles &&
                    this._state == State.Active)
                {
                    // If we get here, it means we already had an active worker which was not monitoring (doing
                    // keepalive). That worker is likely asleep, so we fire state changed to wake it up and have it
                    // switch over to monitoring
                    this.FireStateChangedNoLock();
                }

                return(handle);
            }
        }
        private void ReleaseMonitoringHandle(MonitoringHandle handle)
        {
            lock (this.Lock)
            {
                if (this._monitoringHandleRegistrations !.TryGetValue(handle, out var cancellationTokenSource))
                {
                    this._monitoringHandleRegistrations.Remove(handle);
                    cancellationTokenSource.Dispose();

                    // If we've removed the last reason to be monitoring, fire state changed to stop the monitoring process.
                    // Without this, the next query that attempts to acquire the connection lock will not think we are monitoring
                    // and therefore will not fire state change. Then, it will get stuck waiting for the monitoring query to complete
                    if (this._monitoringHandleRegistrations.Count == 0 && this._state == State.Active)
                    {
                        this.FireStateChangedNoLock();
                    }
                }
            }
        }