From 2f413e7143f231e19b8e838b12f537313cd96884 Mon Sep 17 00:00:00 2001 From: "Dailey, Alaric" Date: Tue, 20 Oct 2026 06:34:19 -0500 Subject: [PATCH] Allow Close to be called more than once DbConnection.Close: "An application can call Close or CloseAsync more than one time. No exception is generated." A second Close returns without raising StateChange again. Co-Authored-By: Claude Opus 5.5 --- DuckDB.NET.Data/DuckDBConnection.cs | 3 ++- DuckDB.NET.Test/DuckDBConnectionTests.cs | 20 ++++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/DuckDB.NET.Data/DuckDBConnection.cs b/DuckDB.NET.Data/DuckDBConnection.cs index 1d350dcf..18769d81 100644 --- a/DuckDB.NET.Data/DuckDBConnection.cs +++ b/DuckDB.NET.Data/DuckDBConnection.cs @@ -83,9 +83,10 @@ public override void ChangeDatabase(string databaseName) public override void Close() { + // DbConnection.Close: "An application can call Close or CloseAsync more than one time. No exception is generated." if (connectionState == ConnectionState.Closed) { - throw new InvalidOperationException("Connection is already closed."); + return; } if (connectionReference is not null) //Should always be the case diff --git a/DuckDB.NET.Test/DuckDBConnectionTests.cs b/DuckDB.NET.Test/DuckDBConnectionTests.cs index a9ce6fae..a446d35a 100644 --- a/DuckDB.NET.Test/DuckDBConnectionTests.cs +++ b/DuckDB.NET.Test/DuckDBConnectionTests.cs @@ -53,30 +53,38 @@ public async Task ConnectionSpeed() files.Dispose(); } + // https://learn.microsoft.com/dotnet/api/system.data.common.dbconnection.close: + // "An application can call Close or CloseAsync more than one time. No exception is generated." [Fact] [Trait("Category", "Baseline")] - public async Task ExceptionOnDoubleClose() + public async Task CloseCanBeCalledMoreThanOnce() { using var dbInfo = DisposableFile.GenerateInTemp("db"); await using var duckDBConnection = new DuckDBConnection(dbInfo.ConnectionString); await duckDBConnection.OpenAsync(); + var stateChanges = 0; + duckDBConnection.StateChange += (_, _) => stateChanges++; await duckDBConnection.CloseAsync(); - await duckDBConnection.Invoking(async connection => await connection.CloseAsync()) - .Should().ThrowAsync(); + await duckDBConnection.CloseAsync(); + duckDBConnection.Close(); + + duckDBConnection.State.Should().Be(ConnectionState.Closed); + stateChanges.Should().Be(1, "only the first Close changes the state"); } [Fact] [Trait("Category", "Baseline")] - public async Task ExceptionOnDisposeThenClose() + public async Task CloseAfterDisposeDoesNotThrow() { using var dbInfo = DisposableFile.GenerateInTemp("db"); await using var duckDBConnection = new DuckDBConnection(dbInfo.ConnectionString); await duckDBConnection.OpenAsync(); await duckDBConnection.DisposeAsync(); - await duckDBConnection.Invoking(async connection => await connection.CloseAsync()) - .Should().ThrowAsync(); + await duckDBConnection.CloseAsync(); + + duckDBConnection.State.Should().Be(ConnectionState.Closed); } ///