Unverified Commit e199a871 authored by Nick Craver's avatar Nick Craver Committed by GitHub

Better error messages for .*Async methods when not using DbConnection (#986)

* Better error messages for .*Async methods when not using  DbConnection

Perhaps in v2 we move these to DbConnection itself ratherr than IDbConnection for the extensions to eliminate the case, but that's breaking...for now we can at least throw a better error. See #757 for details.

* Tweak errors to allow open IDbConnections and those generating DbCommands

Same checks but a bit more lenient with more specific messaging.

* Tweaking error messages and supporting IDbConnection implementations … (#987)
parent abd984d1
...@@ -373,6 +373,36 @@ private static Task<DbDataReader> ExecuteReaderWithFlagsFallbackAsync(DbCommand ...@@ -373,6 +373,36 @@ private static Task<DbDataReader> ExecuteReaderWithFlagsFallbackAsync(DbCommand
return task; return task;
} }
/// <summary>
/// Attempts to open a connection asynchronously, with a better error message for unsupported usages.
/// </summary>
private static Task TryOpenAsync(this IDbConnection cnn, CancellationToken cancel)
{
if (cnn is DbConnection dbConn)
{
return dbConn.OpenAsync(cancel);
}
else
{
throw new InvalidOperationException("Async operations require use of a DbConnection or an already-open IDbConnection");
}
}
/// <summary>
/// Attempts setup a <see cref="DbCommand"/> on a <see cref="DbConnection"/>, with a better error message for unsupported usages.
/// </summary>
private static DbCommand TrySetupAsyncCommand(this CommandDefinition command, IDbConnection cnn, Action<IDbCommand, object> paramReader)
{
if (command.SetupCommand(cnn, paramReader) is DbCommand dbCommand)
{
return dbCommand;
}
else
{
throw new InvalidOperationException("Async operations require use of a DbConnection or an IDbConnection where .CreateCommand() returns a DbCommand");
}
}
private static async Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn, Type effectiveType, CommandDefinition command) private static async Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn, Type effectiveType, CommandDefinition command)
{ {
object param = command.Parameters; object param = command.Parameters;
...@@ -380,12 +410,12 @@ private static async Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn, ...@@ -380,12 +410,12 @@ private static async Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn,
var info = GetCacheInfo(identity, param, command.AddToCache); var info = GetCacheInfo(identity, param, command.AddToCache);
bool wasClosed = cnn.State == ConnectionState.Closed; bool wasClosed = cnn.State == ConnectionState.Closed;
var cancel = command.CancellationToken; var cancel = command.CancellationToken;
using (var cmd = (DbCommand)command.SetupCommand(cnn, info.ParamReader)) using (var cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader))
{ {
DbDataReader reader = null; DbDataReader reader = null;
try try
{ {
if (wasClosed) await ((DbConnection)cnn).OpenAsync(cancel).ConfigureAwait(false); if (wasClosed) await cnn.TryOpenAsync(cancel).ConfigureAwait(false);
reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, CommandBehavior.SequentialAccess | CommandBehavior.SingleResult, cancel).ConfigureAwait(false); reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, CommandBehavior.SequentialAccess | CommandBehavior.SingleResult, cancel).ConfigureAwait(false);
var tuple = info.Deserializer; var tuple = info.Deserializer;
...@@ -444,12 +474,12 @@ private static async Task<T> QueryRowAsync<T>(this IDbConnection cnn, Row row, T ...@@ -444,12 +474,12 @@ private static async Task<T> QueryRowAsync<T>(this IDbConnection cnn, Row row, T
var info = GetCacheInfo(identity, param, command.AddToCache); var info = GetCacheInfo(identity, param, command.AddToCache);
bool wasClosed = cnn.State == ConnectionState.Closed; bool wasClosed = cnn.State == ConnectionState.Closed;
var cancel = command.CancellationToken; var cancel = command.CancellationToken;
using (var cmd = (DbCommand)command.SetupCommand(cnn, info.ParamReader)) using (var cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader))
{ {
DbDataReader reader = null; DbDataReader reader = null;
try try
{ {
if (wasClosed) await ((DbConnection)cnn).OpenAsync(cancel).ConfigureAwait(false); if (wasClosed) await cnn.TryOpenAsync(cancel).ConfigureAwait(false);
reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, (row & Row.Single) != 0 reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, (row & Row.Single) != 0
? CommandBehavior.SequentialAccess | CommandBehavior.SingleResult // need to allow multiple rows, to check fail condition ? CommandBehavior.SequentialAccess | CommandBehavior.SingleResult // need to allow multiple rows, to check fail condition
: CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow, cancel).ConfigureAwait(false); : CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow, cancel).ConfigureAwait(false);
...@@ -546,7 +576,7 @@ private static async Task<int> ExecuteMultiImplAsync(IDbConnection cnn, CommandD ...@@ -546,7 +576,7 @@ private static async Task<int> ExecuteMultiImplAsync(IDbConnection cnn, CommandD
bool wasClosed = cnn.State == ConnectionState.Closed; bool wasClosed = cnn.State == ConnectionState.Closed;
try try
{ {
if (wasClosed) await ((DbConnection)cnn).OpenAsync(command.CancellationToken).ConfigureAwait(false); if (wasClosed) await cnn.TryOpenAsync(command.CancellationToken).ConfigureAwait(false);
CacheInfo info = null; CacheInfo info = null;
string masterSql = null; string masterSql = null;
...@@ -562,7 +592,7 @@ private static async Task<int> ExecuteMultiImplAsync(IDbConnection cnn, CommandD ...@@ -562,7 +592,7 @@ private static async Task<int> ExecuteMultiImplAsync(IDbConnection cnn, CommandD
if (isFirst) if (isFirst)
{ {
isFirst = false; isFirst = false;
cmd = (DbCommand)command.SetupCommand(cnn, null); cmd = command.TrySetupAsyncCommand(cnn, null);
masterSql = cmd.CommandText; masterSql = cmd.CommandText;
var identity = new Identity(command.CommandText, cmd.CommandType, cnn, null, obj.GetType(), null); var identity = new Identity(command.CommandText, cmd.CommandType, cnn, null, obj.GetType(), null);
info = GetCacheInfo(identity, obj, command.AddToCache); info = GetCacheInfo(identity, obj, command.AddToCache);
...@@ -577,7 +607,7 @@ private static async Task<int> ExecuteMultiImplAsync(IDbConnection cnn, CommandD ...@@ -577,7 +607,7 @@ private static async Task<int> ExecuteMultiImplAsync(IDbConnection cnn, CommandD
} }
else else
{ {
cmd = (DbCommand)command.SetupCommand(cnn, null); cmd = command.TrySetupAsyncCommand(cnn, null);
} }
info.ParamReader(cmd, obj); info.ParamReader(cmd, obj);
...@@ -604,7 +634,7 @@ private static async Task<int> ExecuteMultiImplAsync(IDbConnection cnn, CommandD ...@@ -604,7 +634,7 @@ private static async Task<int> ExecuteMultiImplAsync(IDbConnection cnn, CommandD
} }
else else
{ {
using (var cmd = (DbCommand)command.SetupCommand(cnn, null)) using (var cmd = command.TrySetupAsyncCommand(cnn, null))
{ {
foreach (var obj in multiExec) foreach (var obj in multiExec)
{ {
...@@ -640,11 +670,11 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini ...@@ -640,11 +670,11 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
var identity = new Identity(command.CommandText, command.CommandType, cnn, null, param?.GetType(), null); var identity = new Identity(command.CommandText, command.CommandType, cnn, null, param?.GetType(), null);
var info = GetCacheInfo(identity, param, command.AddToCache); var info = GetCacheInfo(identity, param, command.AddToCache);
bool wasClosed = cnn.State == ConnectionState.Closed; bool wasClosed = cnn.State == ConnectionState.Closed;
using (var cmd = (DbCommand)command.SetupCommand(cnn, info.ParamReader)) using (var cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader))
{ {
try try
{ {
if (wasClosed) await ((DbConnection)cnn).OpenAsync(command.CancellationToken).ConfigureAwait(false); if (wasClosed) await cnn.TryOpenAsync(command.CancellationToken).ConfigureAwait(false);
var result = await cmd.ExecuteNonQueryAsync(command.CancellationToken).ConfigureAwait(false); var result = await cmd.ExecuteNonQueryAsync(command.CancellationToken).ConfigureAwait(false);
command.OnCompleted(); command.OnCompleted();
return result; return result;
...@@ -910,8 +940,8 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini ...@@ -910,8 +940,8 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
bool wasClosed = cnn.State == ConnectionState.Closed; bool wasClosed = cnn.State == ConnectionState.Closed;
try try
{ {
if (wasClosed) await ((DbConnection)cnn).OpenAsync(command.CancellationToken).ConfigureAwait(false); if (wasClosed) await cnn.TryOpenAsync(command.CancellationToken).ConfigureAwait(false);
using (var cmd = (DbCommand)command.SetupCommand(cnn, info.ParamReader)) using (var cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader))
using (var reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, CommandBehavior.SequentialAccess | CommandBehavior.SingleResult, command.CancellationToken).ConfigureAwait(false)) using (var reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, CommandBehavior.SequentialAccess | CommandBehavior.SingleResult, command.CancellationToken).ConfigureAwait(false))
{ {
if (!command.Buffered) wasClosed = false; // handing back open reader; rely on command-behavior if (!command.Buffered) wasClosed = false; // handing back open reader; rely on command-behavior
...@@ -960,8 +990,8 @@ private static async Task<IEnumerable<TReturn>> MultiMapAsync<TReturn>(this IDbC ...@@ -960,8 +990,8 @@ private static async Task<IEnumerable<TReturn>> MultiMapAsync<TReturn>(this IDbC
bool wasClosed = cnn.State == ConnectionState.Closed; bool wasClosed = cnn.State == ConnectionState.Closed;
try try
{ {
if (wasClosed) await ((DbConnection)cnn).OpenAsync().ConfigureAwait(false); if (wasClosed) await cnn.TryOpenAsync(command.CancellationToken).ConfigureAwait(false);
using (var cmd = (DbCommand)command.SetupCommand(cnn, info.ParamReader)) using (var cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader))
using (var reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, CommandBehavior.SequentialAccess | CommandBehavior.SingleResult, command.CancellationToken).ConfigureAwait(false)) using (var reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, CommandBehavior.SequentialAccess | CommandBehavior.SingleResult, command.CancellationToken).ConfigureAwait(false))
{ {
var results = MultiMapImpl(null, default(CommandDefinition), types, map, splitOn, reader, identity, true); var results = MultiMapImpl(null, default(CommandDefinition), types, map, splitOn, reader, identity, true);
...@@ -1015,8 +1045,8 @@ public static async Task<GridReader> QueryMultipleAsync(this IDbConnection cnn, ...@@ -1015,8 +1045,8 @@ public static async Task<GridReader> QueryMultipleAsync(this IDbConnection cnn,
bool wasClosed = cnn.State == ConnectionState.Closed; bool wasClosed = cnn.State == ConnectionState.Closed;
try try
{ {
if (wasClosed) await ((DbConnection)cnn).OpenAsync(command.CancellationToken).ConfigureAwait(false); if (wasClosed) await cnn.TryOpenAsync(command.CancellationToken).ConfigureAwait(false);
cmd = (DbCommand)command.SetupCommand(cnn, info.ParamReader); cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader);
reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, CommandBehavior.SequentialAccess, command.CancellationToken).ConfigureAwait(false); reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, CommandBehavior.SequentialAccess, command.CancellationToken).ConfigureAwait(false);
var result = new GridReader(cmd, reader, identity, command.Parameters as DynamicParameters, command.AddToCache, command.CancellationToken); var result = new GridReader(cmd, reader, identity, command.Parameters as DynamicParameters, command.AddToCache, command.CancellationToken);
...@@ -1108,8 +1138,8 @@ private static async Task<IDataReader> ExecuteReaderImplAsync(IDbConnection cnn, ...@@ -1108,8 +1138,8 @@ private static async Task<IDataReader> ExecuteReaderImplAsync(IDbConnection cnn,
bool wasClosed = cnn.State == ConnectionState.Closed; bool wasClosed = cnn.State == ConnectionState.Closed;
try try
{ {
cmd = (DbCommand)command.SetupCommand(cnn, paramReader); cmd = command.TrySetupAsyncCommand(cnn, paramReader);
if (wasClosed) await ((DbConnection)cnn).OpenAsync(command.CancellationToken).ConfigureAwait(false); if (wasClosed) await cnn.TryOpenAsync(command.CancellationToken).ConfigureAwait(false);
var reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, commandBehavior, command.CancellationToken).ConfigureAwait(false); var reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, commandBehavior, command.CancellationToken).ConfigureAwait(false);
wasClosed = false; wasClosed = false;
return reader; return reader;
...@@ -1182,8 +1212,8 @@ private static async Task<T> ExecuteScalarImplAsync<T>(IDbConnection cnn, Comman ...@@ -1182,8 +1212,8 @@ private static async Task<T> ExecuteScalarImplAsync<T>(IDbConnection cnn, Comman
object result; object result;
try try
{ {
cmd = (DbCommand)command.SetupCommand(cnn, paramReader); cmd = command.TrySetupAsyncCommand(cnn, paramReader);
if (wasClosed) await ((DbConnection)cnn).OpenAsync(command.CancellationToken).ConfigureAwait(false); if (wasClosed) await cnn.TryOpenAsync(command.CancellationToken).ConfigureAwait(false);
result = await cmd.ExecuteScalarAsync(command.CancellationToken).ConfigureAwait(false); result = await cmd.ExecuteScalarAsync(command.CancellationToken).ConfigureAwait(false);
command.OnCompleted(); command.OnCompleted();
} }
......
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