I have existing code that looks similar to:
IEnumerable GetStuff()
{
using (SqlConnection conn = new SqlConnection(connectionString))
The problem is what you're asking doesn't actually make much sense. IEnumerable
is a synchronous interface, and returning Task
isn't going to help you much, because some thread would have to block waiting for each item, no matter what.
What you actually want to return is some asynchronous alternative to IEnumerable
: something like IObservable
, dataflow block from TPL Dataflow or IAsyncEnumerable
, which is planned to be added to C# 8.0/.Net Core 3.0. (And in the meantime, there are some libraries that contain it.)
Using TPL Dataflow, one way to do this would be:
ISourceBlock GetStuff() {
var block = new BufferBlock();
Task.Run(async () =>
{
using (SqlConnection conn = new SqlConnection(connectionString))
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
await conn.OpenAsync();
SqlDataReader reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
SomeClass someClass;
// Create an instance of SomeClass based on row returned.
block.Post(someClass);
}
block.Complete();
}
});
return block;
}
You'll probably want to add error handling to the above code, but otherwise, it should work and it will be completely asynchronous.
The rest of your code would then consume items from the returned block also asynchronously, probably using ActionBlock
.