Search code examples
dapperusing

Dapper source code - will this dispose of my connection properly?


Looking at the source code for Dappers QueryAsync method

SqlMapper.Async.cs

 private static async Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn, Type effectiveType, CommandDefinition command)
    {

        using (var cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader))
        {
            DbDataReader reader = null;
            try
            {
                if (wasClosed) await cnn.TryOpenAsync(cancel).ConfigureAwait(false);

                var func = tuple.Func;

                if (command.Buffered)
                {
                    var buffer = new List<T>();
                    var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
                    while (await reader.ReadAsync(cancel).ConfigureAwait(false))
                    {
                        object val = func(reader);
                        if (val == null || val is T)
                        {
                            buffer.Add((T)val);
                        }
                        else
                        {
                            buffer.Add((T)Convert.ChangeType(val, convertToType, CultureInfo.InvariantCulture));
                        }
                    }
                    while (await reader.NextResultAsync(cancel).ConfigureAwait(false)) { /* ignore subsequent result sets */ }
                    command.OnCompleted();
                    return buffer;
                }
                else
                {
                    // can't use ReadAsync / cancellation; but this will have to do
                    wasClosed = false; // don't close if handing back an open reader; rely on the command-behavior
                    var deferred = ExecuteReaderSync<T>(reader, func, command.Parameters);
                    reader = null; // to prevent it being disposed before the caller gets to see it
                    return deferred;
                }
            }
            finally
            {
                using (reader) { /* dispose if non-null */ }
                if (wasClosed) cnn.Close();
            }
        }
    }

Note this line (413):

using (var cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader))

My question is, will my connection be disposed of correctly without me having to wrap my code calling in to this in a using block?


Solution

  • Note following line in the code you post:

    private static async Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn,....
    

    That shows QueryAsync is an extension method of IDbConnection. That means, instance of connection is created in your code somewhere.

    As mentioned here, there are two ways to manage connection with Dapper:

    • Fully manage yourself: Here, you are fully responsible for opening and closing connection. This is just like how you treat connection while working with ADO.NET.

    • Allow Dapper to manage it: Dapper automatically opens the connection (if it was not opened) and closes it (if it was opened by Dapper) for you.

    Considering this, only choice remain here is to open/close connection in your code or allow Dapper to do this for you. If you are doing this yourself then Dapper does not interfere at all.

    If you want to allow Dapper to handle the open/close connection for you and are worried that whether it will close it properly, then yes, it closes it properly.

    Find following code in your post:

    if (wasClosed) await cnn.TryOpenAsync(cancel).ConfigureAwait(false);
    ....
    if (wasClosed) cnn.Close();
    

    Dapper maintains the state/flag in wasClosed variable if connection was opened by Dapper. You can see in your code that the connection is also properly closed at the end. Further, you can further check the Dapper source code to see how the this is handled in multiple methods. Specially check SqlMapper.Async.cs and SqlMapper.cs files.

    Now, this is all about open/close. What about Dispose? Following is what Marc Gravell says in one of the comment for this answer: https://stackoverflow.com/a/12629170/5779732

    well, technically open/closed is different to disposed. If you are only going to be opening/closing around the individual calls, you might as well let dapper do it. If you are opening/closing at a wider granularity (per request, for example), it would be better for your code to do it and pass an open connection to dapper.

    So, if you really want to Dispose the connection instead of just open/close, better you wrap it in using block in your code and pass open connection to Dapper. As mentioned in the comment, this post discusses difference between Dispose and Close.