Sitelet https://github.com/npgsql/npgsql/issues/6149
Skip to content

Using a completed NpgsqlTransaction silently causes command to execute without transaction #6149

Description

@FlushNorris

When assigning an NpgsqlTransaction that has already been committed or rolled back to a NpgsqlCommand, the command executes without exception – but the transaction is silently ignored. This leads to the command being executed outside of any transaction, which is very dangerous and non-obvious.

Repro:

await using var conn = new NpgsqlConnection(...);
await conn.OpenAsync();

var tx = await conn.BeginTransactionAsync();
await tx.RollbackAsync(); // or CommitAsync()

var cmd = conn.CreateCommand();
cmd.Transaction = tx;
cmd.CommandText = "INSERT INTO test_table (value) VALUES ('oops')";
await cmd.ExecuteNonQueryAsync(); // Executes, but silently outside of transaction!

Expected behavior:
Throw an InvalidOperationException if the transaction has already been completed, or
At least log or document this behavior clearly.

Environment:
Npgsql version: 5.0.10
PostgreSQL version: 13.2
.NET version: .net core 6

Activity

  1. vonzshik commented on Jun 30, 2025

    @vonzshik
    Contributor

    It is documented on the property itself.

    /// <summary>
    /// This property is ignored by Npgsql. PostgreSQL only supports a single transaction at a given time on
    /// a given connection, and all commands implicitly run inside the current transaction started via
    /// <see cref="NpgsqlConnection.BeginTransaction()"/>
    /// </summary>
    public new NpgsqlTransaction? Transaction
    {
    get => (NpgsqlTransaction?)DbTransaction;
    set => DbTransaction = value;
    }

    While we also can add that to our documentation, I'm not sure anyone will actually read it just for a basic property like that one.

  2. FlushNorris commented on Jun 30, 2025

    @FlushNorris
    Author

    It might be ignored by Npgsql, but the ExecuteNonQueryAsync and the rest of the Exexcute-methods, should raise an exception if the transaction being provided is ignored due to the Connection is null and IsComplete is true.

    It seems dangerous to do something even though it's clearly not the desired behavior.

  3. vonzshik commented on Jun 30, 2025

    @vonzshik
    Contributor

    While I would rather not check that property every time we execute a query, we can probably assert each time that property is assigned. @roji do you think it makes sense?

  4. roji commented on Jun 30, 2025

    @roji
    Member

    It might be ignored by Npgsql, but the ExecuteNonQueryAsync and the rest of the Exexcute-methods, should raise an exception if the transaction being provided is ignored due to the Connection is null and IsComplete is true.
    It seems dangerous to do something even though it's clearly not the desired behavior.

    Well, is it any different if someone assigns a non-finished NpgsqlTransaction (e.g. that belongs to a completely different connection) and it gets ignored as well? At the end of the day we simlpy ignore that property. Having said that...

    While I would rather not check that property every time we execute a query, we can probably assert each time that property is assigned.

    Yeah, that makes sense to me - a simple InvalidOperationException on assignment sounds reasonable...

  5. self-assigned this
    on Jun 30, 2025
  6. added theissue type on Jun 30, 2025
  7. added this to the 10.0.0 milestone on Jun 30, 2025
  8. added a commit that references this issue on Jul 2, 2025
    3dae121
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions