-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add missing QueryUnbufferedAsync<T> API #1912
Changes from all commits
7bd2bcc
c58806b
3952b58
37ebac2
9cf031c
7616758
c838de4
05d25c8
c362a82
a79b49a
6b8b91c
7ea1ab3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
using System.Data.Common; | ||
using System.Globalization; | ||
using System.Linq; | ||
using System.Runtime.CompilerServices; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
|
@@ -1217,5 +1218,79 @@ private static async Task<T> ExecuteScalarImplAsync<T>(IDbConnection cnn, Comman | |
} | ||
return Parse<T>(result); | ||
} | ||
|
||
#if NET5_0_OR_GREATER | ||
/// <summary> | ||
/// Execute a query asynchronously using <see cref="IAsyncEnumerable{T}"/>. | ||
/// </summary> | ||
/// <typeparam name="T">The type of results to return.</typeparam> | ||
/// <param name="cnn">The connection to query on.</param> | ||
/// <param name="sql">The SQL to execute for the query.</param> | ||
/// <param name="param">The parameters to pass, if any.</param> | ||
/// <param name="transaction">The transaction to use, if any.</param> | ||
/// <param name="commandTimeout">The command timeout (in seconds).</param> | ||
/// <param name="commandType">The type of command to execute.</param> | ||
/// <returns> | ||
/// A sequence of data of <typeparamref name="T"/>; if a basic type (int, string, etc) is queried then the data from the first column is assumed, otherwise an instance is | ||
/// created per row, and a direct column-name===member-name mapping is assumed (case insensitive). | ||
/// </returns> | ||
public static IAsyncEnumerable<T> QueryUnbufferedAsync<T>(this DbConnection cnn, string sql, object param = null, DbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null) | ||
{ | ||
// note: in many cases of adding a new async method I might add a CancellationToken - however, cancellation is expressed via WithCancellation on iterators | ||
return QueryUnbufferedAsync<T>(cnn, typeof(T), new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.None, default)); | ||
} | ||
|
||
private static IAsyncEnumerable<T> QueryUnbufferedAsync<T>(this IDbConnection cnn, Type effectiveType, CommandDefinition command) | ||
{ | ||
return Impl(cnn, effectiveType, command, command.CancellationToken); // proxy to allow CT expression | ||
|
||
static async IAsyncEnumerable<T> Impl(IDbConnection cnn, Type effectiveType, CommandDefinition command, | ||
[EnumeratorCancellation] CancellationToken cancel) | ||
{ | ||
object param = command.Parameters; | ||
var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType()); | ||
var info = GetCacheInfo(identity, param, command.AddToCache); | ||
bool wasClosed = cnn.State == ConnectionState.Closed; | ||
using var cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader); | ||
DbDataReader reader = null; | ||
try | ||
{ | ||
if (wasClosed) await cnn.TryOpenAsync(cancel).ConfigureAwait(false); | ||
reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, CommandBehavior.SequentialAccess | CommandBehavior.SingleResult, cancel).ConfigureAwait(false); | ||
|
||
var tuple = info.Deserializer; | ||
int hash = GetColumnHash(reader); | ||
if (tuple.Func == null || tuple.Hash != hash) | ||
{ | ||
if (reader.FieldCount == 0) | ||
{ | ||
yield break; | ||
} | ||
tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, 0, -1, false)); | ||
if (command.AddToCache) SetQueryCache(identity, info); | ||
} | ||
|
||
var func = tuple.Func; | ||
|
||
var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This yield return GetValue<T>(reader, convertToType, val); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're almost certainly correct; thanks, good eyes! I'll try to look at this tomorrow (I guess I need some tests that do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or maybe it should just be deleted because apparently the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added for tracking: #1920 (because already merged) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
while (await reader.ReadAsync(cancel).ConfigureAwait(false)) | ||
{ | ||
object val = func(reader); | ||
yield return GetValue<T>(reader, effectiveType, val); | ||
} | ||
while (await reader.NextResultAsync(cancel).ConfigureAwait(false)) { /* ignore subsequent result sets */ } | ||
command.OnCompleted(); | ||
} | ||
finally | ||
{ | ||
if (reader is not null) | ||
{ | ||
await reader.DisposeAsync(); | ||
} | ||
if (wasClosed) cnn.Close(); | ||
} | ||
} | ||
} | ||
#endif | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason this is using a
DbConnection
and not anIDbConnection
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Async doesn't work on IDbConnection, it never has. We should have done this with the old methods but can't break things now - IDbConnection simply predates async being a thing, so it never had support. The methods needed are on the abstract base class.