Commit 6754fe27 authored by Joseph Musser's avatar Joseph Musser Committed by Marc Gravell

Add ExecuteReaderAsync overloads that return Task<DbDataReader> (#1295)

parent 8b49eb69
using System;
using System.Threading.Tasks;
namespace Dapper
{
internal static class Extensions
{
/// <summary>
/// Creates a <see cref="Task{TResult}"/> with a less specific generic parameter that perfectly mirrors the
/// state of the specified <paramref name="task"/>.
/// </summary>
internal static Task<TTo> CastResult<TFrom, TTo>(this Task<TFrom> task)
where TFrom : TTo
{
if (task is null) throw new ArgumentNullException(nameof(task));
if (task.Status == TaskStatus.RanToCompletion)
return Task.FromResult((TTo)task.Result);
var source = new TaskCompletionSource<TTo>();
task.ContinueWith(OnTaskCompleted<TFrom, TTo>, state: source, TaskContinuationOptions.ExecuteSynchronously);
return source.Task;
}
private static void OnTaskCompleted<TFrom, TTo>(Task<TFrom> completedTask, object state)
where TFrom : TTo
{
var source = (TaskCompletionSource<TTo>)state;
switch (completedTask.Status)
{
case TaskStatus.RanToCompletion:
source.SetResult(completedTask.Result);
break;
case TaskStatus.Canceled:
source.SetCanceled();
break;
case TaskStatus.Faulted:
source.SetException(completedTask.Exception.InnerExceptions);
break;
}
}
}
}
...@@ -687,7 +687,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini ...@@ -687,7 +687,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
} }
/// <summary> /// <summary>
/// Perform a asynchronous multi-mapping query with 2 input types. /// Perform a asynchronous multi-mapping query with 2 input types.
/// This returns a single type, combined from the raw types via <paramref name="map"/>. /// This returns a single type, combined from the raw types via <paramref name="map"/>.
/// </summary> /// </summary>
/// <typeparam name="TFirst">The first type in the recordset.</typeparam> /// <typeparam name="TFirst">The first type in the recordset.</typeparam>
...@@ -708,7 +708,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini ...@@ -708,7 +708,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn); new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
/// <summary> /// <summary>
/// Perform a asynchronous multi-mapping query with 2 input types. /// Perform a asynchronous multi-mapping query with 2 input types.
/// This returns a single type, combined from the raw types via <paramref name="map"/>. /// This returns a single type, combined from the raw types via <paramref name="map"/>.
/// </summary> /// </summary>
/// <typeparam name="TFirst">The first type in the recordset.</typeparam> /// <typeparam name="TFirst">The first type in the recordset.</typeparam>
...@@ -723,7 +723,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini ...@@ -723,7 +723,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
MultiMapAsync<TFirst, TSecond, DontMap, DontMap, DontMap, DontMap, DontMap, TReturn>(cnn, command, map, splitOn); MultiMapAsync<TFirst, TSecond, DontMap, DontMap, DontMap, DontMap, DontMap, TReturn>(cnn, command, map, splitOn);
/// <summary> /// <summary>
/// Perform a asynchronous multi-mapping query with 3 input types. /// Perform a asynchronous multi-mapping query with 3 input types.
/// This returns a single type, combined from the raw types via <paramref name="map"/>. /// This returns a single type, combined from the raw types via <paramref name="map"/>.
/// </summary> /// </summary>
/// <typeparam name="TFirst">The first type in the recordset.</typeparam> /// <typeparam name="TFirst">The first type in the recordset.</typeparam>
...@@ -745,7 +745,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini ...@@ -745,7 +745,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn); new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
/// <summary> /// <summary>
/// Perform a asynchronous multi-mapping query with 3 input types. /// Perform a asynchronous multi-mapping query with 3 input types.
/// This returns a single type, combined from the raw types via <paramref name="map"/>. /// This returns a single type, combined from the raw types via <paramref name="map"/>.
/// </summary> /// </summary>
/// <typeparam name="TFirst">The first type in the recordset.</typeparam> /// <typeparam name="TFirst">The first type in the recordset.</typeparam>
...@@ -761,7 +761,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini ...@@ -761,7 +761,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
MultiMapAsync<TFirst, TSecond, TThird, DontMap, DontMap, DontMap, DontMap, TReturn>(cnn, command, map, splitOn); MultiMapAsync<TFirst, TSecond, TThird, DontMap, DontMap, DontMap, DontMap, TReturn>(cnn, command, map, splitOn);
/// <summary> /// <summary>
/// Perform a asynchronous multi-mapping query with 4 input types. /// Perform a asynchronous multi-mapping query with 4 input types.
/// This returns a single type, combined from the raw types via <paramref name="map"/>. /// This returns a single type, combined from the raw types via <paramref name="map"/>.
/// </summary> /// </summary>
/// <typeparam name="TFirst">The first type in the recordset.</typeparam> /// <typeparam name="TFirst">The first type in the recordset.</typeparam>
...@@ -784,7 +784,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini ...@@ -784,7 +784,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn); new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
/// <summary> /// <summary>
/// Perform a asynchronous multi-mapping query with 4 input types. /// Perform a asynchronous multi-mapping query with 4 input types.
/// This returns a single type, combined from the raw types via <paramref name="map"/>. /// This returns a single type, combined from the raw types via <paramref name="map"/>.
/// </summary> /// </summary>
/// <typeparam name="TFirst">The first type in the recordset.</typeparam> /// <typeparam name="TFirst">The first type in the recordset.</typeparam>
...@@ -801,7 +801,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini ...@@ -801,7 +801,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
MultiMapAsync<TFirst, TSecond, TThird, TFourth, DontMap, DontMap, DontMap, TReturn>(cnn, command, map, splitOn); MultiMapAsync<TFirst, TSecond, TThird, TFourth, DontMap, DontMap, DontMap, TReturn>(cnn, command, map, splitOn);
/// <summary> /// <summary>
/// Perform a asynchronous multi-mapping query with 5 input types. /// Perform a asynchronous multi-mapping query with 5 input types.
/// This returns a single type, combined from the raw types via <paramref name="map"/>. /// This returns a single type, combined from the raw types via <paramref name="map"/>.
/// </summary> /// </summary>
/// <typeparam name="TFirst">The first type in the recordset.</typeparam> /// <typeparam name="TFirst">The first type in the recordset.</typeparam>
...@@ -825,7 +825,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini ...@@ -825,7 +825,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn); new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
/// <summary> /// <summary>
/// Perform a asynchronous multi-mapping query with 5 input types. /// Perform a asynchronous multi-mapping query with 5 input types.
/// This returns a single type, combined from the raw types via <paramref name="map"/>. /// This returns a single type, combined from the raw types via <paramref name="map"/>.
/// </summary> /// </summary>
/// <typeparam name="TFirst">The first type in the recordset.</typeparam> /// <typeparam name="TFirst">The first type in the recordset.</typeparam>
...@@ -843,7 +843,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini ...@@ -843,7 +843,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
MultiMapAsync<TFirst, TSecond, TThird, TFourth, TFifth, DontMap, DontMap, TReturn>(cnn, command, map, splitOn); MultiMapAsync<TFirst, TSecond, TThird, TFourth, TFifth, DontMap, DontMap, TReturn>(cnn, command, map, splitOn);
/// <summary> /// <summary>
/// Perform a asynchronous multi-mapping query with 6 input types. /// Perform a asynchronous multi-mapping query with 6 input types.
/// This returns a single type, combined from the raw types via <paramref name="map"/>. /// This returns a single type, combined from the raw types via <paramref name="map"/>.
/// </summary> /// </summary>
/// <typeparam name="TFirst">The first type in the recordset.</typeparam> /// <typeparam name="TFirst">The first type in the recordset.</typeparam>
...@@ -868,7 +868,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini ...@@ -868,7 +868,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn); new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
/// <summary> /// <summary>
/// Perform a asynchronous multi-mapping query with 6 input types. /// Perform a asynchronous multi-mapping query with 6 input types.
/// This returns a single type, combined from the raw types via <paramref name="map"/>. /// This returns a single type, combined from the raw types via <paramref name="map"/>.
/// </summary> /// </summary>
/// <typeparam name="TFirst">The first type in the recordset.</typeparam> /// <typeparam name="TFirst">The first type in the recordset.</typeparam>
...@@ -887,7 +887,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini ...@@ -887,7 +887,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
MultiMapAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, DontMap, TReturn>(cnn, command, map, splitOn); MultiMapAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, DontMap, TReturn>(cnn, command, map, splitOn);
/// <summary> /// <summary>
/// Perform a asynchronous multi-mapping query with 7 input types. /// Perform a asynchronous multi-mapping query with 7 input types.
/// This returns a single type, combined from the raw types via <paramref name="map"/>. /// This returns a single type, combined from the raw types via <paramref name="map"/>.
/// </summary> /// </summary>
/// <typeparam name="TFirst">The first type in the recordset.</typeparam> /// <typeparam name="TFirst">The first type in the recordset.</typeparam>
...@@ -913,7 +913,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini ...@@ -913,7 +913,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn); new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
/// <summary> /// <summary>
/// Perform an asynchronous multi-mapping query with 7 input types. /// Perform an asynchronous multi-mapping query with 7 input types.
/// This returns a single type, combined from the raw types via <paramref name="map"/>. /// This returns a single type, combined from the raw types via <paramref name="map"/>.
/// </summary> /// </summary>
/// <typeparam name="TFirst">The first type in the recordset.</typeparam> /// <typeparam name="TFirst">The first type in the recordset.</typeparam>
...@@ -956,7 +956,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini ...@@ -956,7 +956,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
} }
/// <summary> /// <summary>
/// Perform a asynchronous multi-mapping query with an arbitrary number of input types. /// Perform a asynchronous multi-mapping query with an arbitrary number of input types.
/// This returns a single type, combined from the raw types via <paramref name="map"/>. /// This returns a single type, combined from the raw types via <paramref name="map"/>.
/// </summary> /// </summary>
/// <typeparam name="TReturn">The combined type to return.</typeparam> /// <typeparam name="TReturn">The combined type to return.</typeparam>
...@@ -1101,6 +1101,18 @@ public static async Task<GridReader> QueryMultipleAsync(this IDbConnection cnn, ...@@ -1101,6 +1101,18 @@ public static async Task<GridReader> QueryMultipleAsync(this IDbConnection cnn,
/// </code> /// </code>
/// </example> /// </example>
public static Task<IDataReader> ExecuteReaderAsync(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null) => public static Task<IDataReader> ExecuteReaderAsync(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null) =>
ExecuteWrappedReaderImplAsync(cnn, new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.Buffered), CommandBehavior.Default).CastResult<DbDataReader, IDataReader>();
/// <summary>
/// Execute parameterized SQL and return a <see cref="DbDataReader"/>.
/// </summary>
/// <param name="cnn">The connection to execute on.</param>
/// <param name="sql">The SQL to execute.</param>
/// <param name="param">The parameters to use for this command.</param>
/// <param name="transaction">The transaction to use for this command.</param>
/// <param name="commandTimeout">Number of seconds before command execution timeout.</param>
/// <param name="commandType">Is it a stored proc or a batch?</param>
public static Task<DbDataReader> ExecuteReaderAsync(this DbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null) =>
ExecuteWrappedReaderImplAsync(cnn, new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.Buffered), CommandBehavior.Default); ExecuteWrappedReaderImplAsync(cnn, new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.Buffered), CommandBehavior.Default);
/// <summary> /// <summary>
...@@ -1114,6 +1126,14 @@ public static async Task<GridReader> QueryMultipleAsync(this IDbConnection cnn, ...@@ -1114,6 +1126,14 @@ public static async Task<GridReader> QueryMultipleAsync(this IDbConnection cnn,
/// or <see cref="T:DataSet"/>. /// or <see cref="T:DataSet"/>.
/// </remarks> /// </remarks>
public static Task<IDataReader> ExecuteReaderAsync(this IDbConnection cnn, CommandDefinition command) => public static Task<IDataReader> ExecuteReaderAsync(this IDbConnection cnn, CommandDefinition command) =>
ExecuteWrappedReaderImplAsync(cnn, command, CommandBehavior.Default).CastResult<DbDataReader, IDataReader>();
/// <summary>
/// Execute parameterized SQL and return a <see cref="DbDataReader"/>.
/// </summary>
/// <param name="cnn">The connection to execute on.</param>
/// <param name="command">The command to execute.</param>
public static Task<DbDataReader> ExecuteReaderAsync(this DbConnection cnn, CommandDefinition command) =>
ExecuteWrappedReaderImplAsync(cnn, command, CommandBehavior.Default); ExecuteWrappedReaderImplAsync(cnn, command, CommandBehavior.Default);
/// <summary> /// <summary>
...@@ -1128,9 +1148,18 @@ public static async Task<GridReader> QueryMultipleAsync(this IDbConnection cnn, ...@@ -1128,9 +1148,18 @@ public static async Task<GridReader> QueryMultipleAsync(this IDbConnection cnn,
/// or <see cref="T:DataSet"/>. /// or <see cref="T:DataSet"/>.
/// </remarks> /// </remarks>
public static Task<IDataReader> ExecuteReaderAsync(this IDbConnection cnn, CommandDefinition command, CommandBehavior commandBehavior) => public static Task<IDataReader> ExecuteReaderAsync(this IDbConnection cnn, CommandDefinition command, CommandBehavior commandBehavior) =>
ExecuteWrappedReaderImplAsync(cnn, command, commandBehavior).CastResult<DbDataReader, IDataReader>();
/// <summary>
/// Execute parameterized SQL and return a <see cref="DbDataReader"/>.
/// </summary>
/// <param name="cnn">The connection to execute on.</param>
/// <param name="command">The command to execute.</param>
/// <param name="commandBehavior">The <see cref="CommandBehavior"/> flags for this reader.</param>
public static Task<DbDataReader> ExecuteReaderAsync(this DbConnection cnn, CommandDefinition command, CommandBehavior commandBehavior) =>
ExecuteWrappedReaderImplAsync(cnn, command, commandBehavior); ExecuteWrappedReaderImplAsync(cnn, command, commandBehavior);
private static async Task<IDataReader> ExecuteWrappedReaderImplAsync(IDbConnection cnn, CommandDefinition command, CommandBehavior commandBehavior) private static async Task<DbDataReader> ExecuteWrappedReaderImplAsync(IDbConnection cnn, CommandDefinition command, CommandBehavior commandBehavior)
{ {
Action<IDbCommand, object> paramReader = GetParameterReader(cnn, ref command); Action<IDbCommand, object> paramReader = GetParameterReader(cnn, ref command);
......
...@@ -93,6 +93,14 @@ public static IDataReader Create(IDbCommand cmd, IDataReader reader) ...@@ -93,6 +93,14 @@ public static IDataReader Create(IDbCommand cmd, IDataReader reader)
cmd.Dispose(); cmd.Dispose();
return null; // GIGO return null; // GIGO
} }
public static DbDataReader Create(IDbCommand cmd, DbDataReader reader)
{
if (cmd == null) return reader; // no need to wrap if no command
if (reader != null) return new DbWrappedReader(cmd, reader);
cmd.Dispose();
return null; // GIGO
}
} }
internal sealed class DbWrappedReader : DbDataReader, IWrappedDataReader internal sealed class DbWrappedReader : DbDataReader, IWrappedDataReader
{ {
...@@ -143,7 +151,7 @@ protected override void Dispose(bool disposing) ...@@ -143,7 +151,7 @@ protected override void Dispose(bool disposing)
_cmd = null; _cmd = null;
} }
} }
public override int FieldCount => _reader.FieldCount; public override int FieldCount => _reader.FieldCount;
public override bool GetBoolean(int i) => _reader.GetBoolean(i); public override bool GetBoolean(int i) => _reader.GetBoolean(i);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment