Dapper's nested `using` clause - Clarification? - c#

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.

Related

How do you use just "using" for npgsql instead of trycatchfinally?

In one of my previous questions someone suggested that instead of using trycatch and closing the connection at finally I should just use "using". Can someone give me an example?
I have this code, how do I use just "using" with it?
try
{
conn3.Open();
string sql_check = "SELECT (time_in) FROM timeinout WHERE employee_id = #employee_id AND date = #date";
using var cmd_check = new NpgsqlCommand(sql_check, conn3);
cmd_check.Parameters.AddWithValue("employee_id", id);
cmd_check.Parameters.AddWithValue("date", date);
cmd_check.Prepare();
var reader = cmd_check.ExecuteReader();
if (reader.Read())
{
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
finally
{
conn3.Close();
}
using is not a replacement for try...catch...finally. It enables you to do away with the finally block but, if an exception can be thrown, you still need to catch it. The using block guarantees that the object created will be disposed, whether an exception is thrown or not. This:
var obj = new SomeType();
try
{
// Use obj here.
}
catch
{
// Handle exception here.
}
finally
{
obj.Dispose();
}
can AND SHOULD be replaced with this:
using (obj = new SomeType())
{
try
{
// Use obj here.
}
catch
{
// Handle exception here.
}
}
Basically, if already have exception handling then you still need exception handling but you don't need to close/dispose locally-created objects in a finally block. If you're doing anything else in your finally block then you still need it too.
In your case, you should be creating your connection, command and data reader with using statements:
using (var connection = new NpgsqlConnection(connectionString))
using (var command = new NpgsqlCommand(query, connection))
{
try
{
connection.Open();
using (var reader = command.ExecuteReader())
{
return reader.HasRows;
}
}
catch
{
return false;
}
}
The data reader will be closed at the end of the inner using block and the connection will be closed at the end of the outer using block.

Is it possible to return a pointer to an IAsyncEnumerable in an async method?

I have an async method that processes a datareader with a delegate function that is passed to it. The purpose of the delegate is to construct domain objects out of the reader and yield those back to the caller. I would like an intermediate method that constructs the delegate and reader, and returns the resulting IAsyncEnumerable from the called method. The only way I was able to make this happen is to actually consume the IAsyncEnumerable and yield those results from the intermediate method. Attempting to just return directly results in a compiler error stating I must use yield return or yield break.
delegate T ProcessFunc<T>(MySqlDataReader reader);
async IAsyncEnumerable<T> ProcessReader<T>(MySqlDataReader reader, ProcessFunc<T> transformFunc)
{
while (await reader.ReadAsync() != false)
{
yield return transformFunc(reader);
}
await reader.DisposeAsync();
}
public async IAsyncEnumerable<DataObject> GetDataObjectsAsync()
{
ProcessFunc<DataObject> processFunc = (reader) =>
{
var id = reader.GetGuid( "id" );
return new DataObject(id);
};
var reader = await GetDataObjectsReaderAsync(); //Constructs appropriate sqlcommand and returns a mysqldatareader
//only way i can get this to function
//would like to just be able to write: return ProcessReader(reader, processFunc)
//so as not to chain another enumerable
await foreach (var obj in ProcessReader( reader, processFunc ))
yield return obj;
}
In this case you can change your ProcessReader to accept Task<MySqlDataReader> instead of MySqlDataReader so you can make your GetDataObjectsAsync synchronous:
async IAsyncEnumerable<T> ProcessReader<T>(Task<MySqlDataReader> readerTask, ProcessFunc<T> transformFunc)
{
var reader = await readerTask;
while (await reader.ReadAsync() != false)
{
yield return transformFunc(reader);
}
await reader.DisposeAsync();
}
public IAsyncEnumerable<DataObject> GetDataObjects()
{
ProcessFunc<DataObject> processFunc = (reader) =>
{
var id = reader.GetGuid( "id" );
return new DataObject(id);
};
return ProcessReader(GetDataObjectsReaderAsync(), processFunc)
}
Or change your GetDataObjectsAsync method to return Task<IAsyncEnumerable<DataObject>>:
public async Task<IAsyncEnumerable<DataObject>> GetDataObjectsAsync()
{
ProcessFunc<DataObject> processFunc = (reader) =>
{
var id = reader.GetGuid( "id" );
return new DataObject(id);
};
var reader = await GetDataObjectsReaderAsync(); //Constructs appropriate sqlcommand and returns a mysqldatareader
return ProcessReader(reader, processFunc);
}

how to fire a callback once data reader reads all result

I have a class like
class ReadData{
public IdataReader Execute(string sql){
// Ado.net code here
return cmd.ExecuteReader();
}
}
This is the sample implementation and it works fine.
I am calling like this in caller
class caller{
void CallMethod(){
var reader = Execute("Sql query here");
while(reader.Read()){
logic here
}
//Here i need to get the out params after reading the resultset.
//But the impplementation should in the class ReadData.
//because that class has implementation to get the out params for the
//other type means, execute without resultset get only output params
}
}
Some possible ways like calling the first method with callback and in that once the data is read completely then read the out params.
I don't know how to implement that stuff.
Any possible better ways to do ?
Please help me on this..
public void Execute(string sql, Action<IDataRecord> action)
{
using(var connection = new ...)
{
connection.Open();
using(var command = new ...)
{
using(var reader = command.ExecuteReader())
{
while(reader.Read())
{
action(reader);
}
}
}
}
}
This would allow you to do something like this:
var entries = List<object>();
Execute("Sql query here", row => entries.Add(row["Field"]));
Or you could try a more linqy appraoch:
public IEnumerable<IDataRecord> Execute(string sql)
{
using(var connection = new ...)
{
connection.Open();
using(var command = new ...)
{
using(var reader = command.ExecuteReader())
{
while(reader.Read())
{
yield return reader;
}
}
}
}
}
Which would allow something like this:
var list = Execute("Sql query here").Where(row => (int)row["Field"] == 17)).ToList();
However, this has some weird effects with defered execution if you don't materialize it properly.

Are my connections being returned to the pool if I don't Dispose my IDataReader?

I'm trying to determine if there are glaring errors in a code base, or not.
The code in question calls a third party dll which returns an IDataReader. If the code uses the reader without disposing of it, it won't be explicitly returned to the pool, correct?
Here's the calling code:
IDataReader rdr = db.ExecSPGetDataReader("dbo.someStoredProcedure", paramList);
if (rdr.Read())
{
List<nameValuePair> formValues = Utils.nameValuePairs(rdr["valuepairs"].ToString());
foreach (nameValuePair nvp in formValues)
{
if (nvp.name.ToLower() == "name")
{
outString = nvp.value;
break;
}
}
}
Here's the decompiled third party dll code:
public IDataReader ExecSPGetDataReader(string sp, List<param> paramList)
{
IDataReader dataReader;
using (DbCommand dbC = this.setDBCommand(sp, paramList, true))
{
IDataReader dr = this._db.ExecuteReader(dbC);
this.setOutputParams(dbC, paramList);
dataReader = dr;
}
return dataReader;
}
It looks like the command gets disposed of, probably for the purpose of disposing the connection, but if that's true, how can anything be read from the returned IDataReader?
If the code uses the reader without disposing of it, it won't be explicitly returned to the pool, correct?
That is correct. Change the code to this:
using (IDataReader rdr =
db.ExecSPGetDataReader("dbo.someStoredProcedure", paramList))
{
if (rdr.Read())
{
List<nameValuePair> formValues =
Utils.nameValuePairs(rdr["valuepairs"].ToString());
foreach (nameValuePair nvp in formValues)
{
if (nvp.name.ToLower() == "name")
{
outString = nvp.value;
break;
}
}
}
}
The using statement will ensure that Dispose is called.

Why does my successfully-read Npgsql data disappear?

I have the following code shape. It seems that I'm misunderstanding the C# method return values. How is it possible that a "full" enumerator gets returned as an empty one?
class ThingDoer
{
public NpgsqlDataReader DoQuery()
{
NpgsqlCommand c = new NpgsqlCommand(...);
NpgsqlDataReader dataread = c.ExecuteReader();
return dataread; // Debugger confirms that six data are enumerable here.
}
}
...
class OtherThing
{
public void higherLevelFunction()
{
NpgsqlDataReader result = myThingDoer.DoQuery();
result.Read(); // No data! result's enumerable returns nothing!
}
}
You don't detail where your connection is coming from. Assuming it's something like:
public NpgsqlDataReader DoQuery()
{
using(NpgsqlConnection = GetConnectionCode())
{
NpgsqlCommand c = new NpgsqlCommand(...);
NpgsqlDataReader dataread = c.ExecuteReader();
return dataread;
}//Connection closes at this using-scope being left because that triggers Dispose()
}
Then change it to:
public NpgsqlDataReader DoQuery()
{
bool ownershipPassed = false;
NpgsqlConnection conn = GetConnectionCode();
try
{
NpgsqlCommand c = new NpgsqlCommand(...);
NpgsqlDataReader dataread = c.ExecuteReader(CommandBehavior.CloseConnection);
ownershipPassed = true;
return dataread;
}
finally
{
if(!ownershipPassed)//only if we didn't create the reader than takes charge of the connection
conn.Dispose();
}
}
Then where you use the reader, you have to dispose it to in turn dispose the connection's underlying connection to the database:
public void higherLevelFunction()
{
using(NpgsqlDataReader result = myThingDoer.DoQuery())
result.Read();
}
NpgsqlCommand c = new NpgsqlCommand(...);
NpgsqlDataReader dataread = c.ExecuteReader();
The above lines are very local to the method DoQuery. So as soon as the control comes out of the method, every object created inside to this method loses its scope. Hence you're losing the data because it's a reference type you're referring to in the caller method.

Categories