I'm learning how Dapper is working behind the scenes.
However I saw this pattern of disposing which is not understood to me.
Roughly in general — this is how QueryAsync
is implemented :
/*1*/ public async Task<IEnumerable<T>> QueryAsync<T>(string sql, Func<IDataRecord, T> projector, DbConnection _conn, dynamic param = null)
/*2*/ {
/*3*/
/*4*/ DbDataReader reader = null;
/*5*/ bool wasClosed = _conn.State == ConnectionState.Closed;
/*6*/ try
/*7*/ {
/*8*/
/*9*/ using (var cmd = _conn.CreateCommand())
/*10*/ {
/*11*/ if (param!=null)
/*12*/ foreach (var prop in param.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
/*13*/ {
/*14*/ var parameter = cmd.CreateParameter();
/*15*/ parameter.ParameterName = prop.Name;
/*16*/ parameter.Value = prop.GetValue(param, null);
/*17*/ cmd.Parameters.Add(parameter);
/*18*/ }
/*19*/
/*20*/ await _conn.OpenAsync().ConfigureAwait(false);
/*21*/ cmd.CommandTimeout = 100000;
/*22*/ cmd.CommandText = sql;
/*23*/ reader = await cmd.ExecuteReaderAsync().ConfigureAwait(false);
/*24*/ List<T> buffer = new List<T>();
/*25*/ while (await reader.ReadAsync().ConfigureAwait(false)) buffer.Add(projector(reader));
/*26*/ return buffer;
/*27*/ }
/*28*/
/*29*/ }
/*30*/ finally
/*31*/ {
/*32*/ using (reader) { }
/*33*/ if (wasClosed) _conn.Close();
/*34*/ }
/*35*/ }
I can understand why he didn't use using
over the connection , that's because he wanted to conditionally close the connection via the wasClosed variable.
For doing it - he must use the try/finally
clause. ( so the conditional closing will be in the finally
clause)
But my question is about line #32.
Instead of doing using at the finally clause , he could do:
using (DbDataReader reader = await cmd.ExecuteReaderAsync().ConfigureAwait(false))
{
List<T> buffer = new List<T>();
while (await reader.ReadAsync().ConfigureAwait(false)) buffer.Add(projector(reader));
return buffer;
}
So the finally
clause is left with :
finally
{
//using (reader) { } //removed
if (wasClosed) _conn.Close();
}
Question
I've seen this using
clause in a finally clause many times in dapper.
I must be missing something here, But what does this pattern achieve that my suggestion does not?
I'm no @MarcGravell, but I think there is one thing you're missing. The code you pasted doesn't exactly match the link you reference. The relevant code path looks like this:
try
{
if (command.Buffered)
{
List<T> buffer = new List<T>();
while (await reader.ReadAsync(cancel).ConfigureAwait(false))
{
buffer.Add((T)func(reader));
}
while (await reader.NextResultAsync().ConfigureAwait(false)) { }
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();
}
The method can either return a buffered result (indicated by the command.Buffered
flag) or a deferred iterator. If Marc was to wrap the reader with a using
statement and return an iterator, it (the reader) would of been disposed by the time the call-site executed it. By setting the reader to null
(in the line before he returns the deferred result) he prevents the reader from being disposed, because the using in the finally block would be translated to this:
finally
{
IDisposable disposable = reader;
try
{
}
finally
{
if (dispoable != null)
{
disposable.Dispose();
}
}
}
When he sets the reader to null
, it isn't disposed, and the reference exists in the iterator is still alive, pointing to the reader. This way, he can both dispose the reader in the normal codepath, but keep it alive if a deferred iterator was requested.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With