I'm working with .Net Core 5.0 and Dapper as ORM.
I have the following c# code:
public Task<IEnumerable<FooViewModel>> FetchAllFoos1(CancellationToken cancel = default)
{
string sql = "SELECT * FROM Foos";
var context = new DbContext();
var connection = context.GetConnection();
var cmd = new CommandDefinition(sql, cancellationToken: cancel);
return connection.QueryAsync<Foo>(cmd)
.ContinueWith(x => x.Result.Select(y => ToFooViewModel(y)), cancel);
}
This code is working perfectly.
But this one not and I don't understand why:
public Task<IEnumerable<FooViewModel>> FetchAllFoos2(CancellationToken cancel = default)
{
string sql = "SELECT * FROM Foos";
using (var context = new DbContext())
{
using (var connection = context.GetConnection())
{
var cmd = new CommandDefinition(sql, cancellationToken: cancel);
return connection.QueryAsync<Foo>(cmd)
.ContinueWith(x => x.Result.Select(y => ToFooViewModel(y)), cancel);
}
}
}
When awaiting the result of FetchAllFoos2: var result = await FetchAllFoos2(), i have a Task Cancelled exception. It happens in the ContinueWith, when it's trying to get x.Result.
I know that the issue come from because i'm using "using" that close the context/connection, but i don't undestand the inner reasons of the exception. I like to use "using" to make sure that any disposable object is cleaned when i'm over the control of the using, but it seems that i cannot use it here..
Can you help me to understand ?
Thank you.
As TheGeneral pointed out, the core problem is that you're using the dangerous, low-level ContinueWith
method. As a general rule, use await
instead of ContinueWith
.
public async Task<IEnumerable<FooViewModel>> FetchAllFoos2(CancellationToken cancel = default)
{
string sql = "SELECT * FROM Foos";
using (var context = new DbContext())
{
using (var connection = context.GetConnection())
{
var cmd = new CommandDefinition(sql, cancellationToken: cancel);
var result = await connection.QueryAsync<Foo>(cmd);
return result.Select(y => ToFooViewModel(y));
}
}
}
One of the problems of skipping async
and await
is that things like disposal happen at an incorrect time. For the async
method, the disposals happen after the data is retrieved (after the await
). For the non-async
method, the disposals happen after the query is started but (possibly) before it returns its data.