jonwagner / Insight.Database

Fast, lightweight .NET micro-ORM
Other
860 stars 145 forks source link

"Async" versions of ForEach, ForEachSql, and AsEnumerable #374

Closed dbakuntsev closed 6 years ago

dbakuntsev commented 6 years ago

Type

What kind of issue is this?

[ ] Bug report.
[x] Feature request.

Current Behavior

"Async" versions of ForEach, ForEachSql, and AsEnumerable extensions are missing. It would be very helpful to have an optional CancellationToken parameter in these functions, too!

jonwagner commented 6 years ago

AsEnumerable doesn't rely on any external dependencies, so it doesn't need an async version.

I thought about making async versions of ForEach as they are currently written, but it would call back to user code, which could be blocking on other things, and I think people would be more likely to shoot themselves with deadlocks. (And it was pretty icky when I added them back in .NET 3.0)

With modern c#, you can just do:

var items = await connection.QuerySqlAsync<>(...);
items.ForEach(i => await action(i));

Alternatively, I could make ForEachAsync use ToListAsync instead of AsEnumerable, and that would make it somewhat safe, but I'm not sure that it buys you much.

Do you have a use case where this would be helpful?

dbakuntsev commented 6 years ago

Thank you for the response!

AsEnumerable actually does have an external dependency on ReadAsync. That's the call I want to abort with the CancellationToken, I'm not as concerned with the loop itself.

The use case is simple, really. It happens all the time in ETL scenarios.

  1. Determine that a query returns a result that may not fit into the process memory, so use the streaming technique.
  2. Do foreach (var item in connection.AsEnumerable<>(...)) on a query that takes a while to return the first result.
  3. Attempt to cancel the loop before the first result returns.

As currently implemented, the thread is blocked retrieving the first entry from the enumerable, there's no way to cancel that wait but for the server to start returning results, or for the command to time out.

jonwagner commented 6 years ago

ah yes, that makes sense. Let's see what we can do about that.

jonwagner commented 6 years ago

The code in the 6.2.4 branch has an AsEnumerableAsync method. I made ToListAsync use it, so it should be working. It checks the cancellation token before it reads the result schema and also passes the token to the Read and NextResult calls. I think it should make things cancellable for you.

Give it a shot. I'll be doing an official build in the next day or two.

jonwagner commented 6 years ago

These are now available in v6.2.4