DapperLib / DapperLib/Dapper

Rainbow.Dapper.Database.Dispose(true) should never throw an exception

Open
#1,252 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area:rainbow
Dominant language
C#
Stars
18.4k
Forks
3.7k
Avg merge
5h 8m
Merged PRs (30d)
1

Description

The current implementation of this method is the following:

      public void Dispose()
       {
           if (_connection.State != ConnectionState.Closed)
           {
               _transaction?.Rollback();

               _connection.Close();
               _connection = null;
           }
       }

Here's one problem. If this method is called in "using" statement or as a part of an explicit "finally" block it may happen so that it will fail and throw and exception of its own, thus, hiding the original exception.

Here's one case i'm getting from time to time:

System.InvalidOperationException: This SqlTransaction has completed; it is no longer usable.
   at System.Data.SqlClient.SqlTransaction.ZombieCheck()
   at System.Data.SqlClient.SqlTransaction.Rollback()
   at Dapper.Database`1.Dispose() in C:\projects\dapper\Dapper.Rainbow\Database.cs:line 473
   at MyClass.MyMethod() in D:\MyClass.cs:line 666

Here, as we can see the original exception is lost forever. The only work-around I can invent is to do it like this (pseudo-code):

using (db = NewDb())
{
    try
    {
        db.BeginTransaction();
        // Useful work
        db.CommitTransaction();
    }
    catch (ex)
    {
        Log(ex);
        throw;
    }
}

which kind of sucks, especially taking into account that we need to put this boiler plate in every place we use Rainbow.Database.

Actually, it's a good practice to not throw from Dispose(true).

Thoughts?

I can provide a PR if it's going to be accepted.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in Dapper.Rainbow/Database.cs at Database.Dispose(), using the reported SqlTransaction.Rollback stack trace as the entry point. Review how disposal interacts with completed transactions and connection closing. Done means disposing after a transaction failure or completion does not throw its own exception or hide the caller's original exception.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, sql
Domain
backend, database
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.