DapperLib/Dapper · error · InvalidOperationException
Async operations require use of a DbConnection or an IDbConn
Error message
Async operations require use of a DbConnection or an IDbConnection where .CreateCommand() returns a DbCommand
What it means
TrySetupAsyncCommand throws InvalidOperationException("Async operations require use of a DbConnection or an IDbConnection where .CreateCommand() returns a DbCommand") when the command built via SetupCommand is not a DbCommand. Dapper's async execution needs DbCommand for ExecuteReaderAsync etc.; a connection whose CreateCommand yields a plain IDbCommand cannot drive async I/O. The message is an intentional, clearer replacement for an InvalidCastException.
Source
Thrown at Dapper/SqlMapper.Async.cs:418
}
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)
{
object? param = command.Parameters;
var identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, effectiveType, param?.GetType());
var info = GetCacheInfo(identity, param, command.AddToCache);
bool wasClosed = cnn.State == ConnectionState.Closed;
var cancel = command.CancellationToken;
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;
View on GitHub (pinned to 72a54c475f)
Solutions
- Use a standard provider whose command derives from System.Data.Common.DbCommand (all mainstream providers do).
- In tests, mock DbConnection/DbCommand rather than the raw IDb* interfaces, or use an in-memory provider like Microsoft.Data.Sqlite.
Example fix
// before (test mock) var mockConn = Substitute.For<IDbConnection>(); mockConn.CreateCommand().Returns(mockCmd); // mockCmd is IDbCommand await mockConn.QueryAsync<T>(sql); // throws // after var mockConn = Substitute.For<DbConnection>(); // provide DbCommand-derived mock; or use a real in-memory provider
Defensive patterns
Strategy: type-guard
Validate before calling
if (cnn is not System.Data.Common.DbConnection || cnn.CreateCommand() is not System.Data.Common.DbCommand)
throw new InvalidOperationException("Async Dapper requires a connection whose CreateCommand returns a DbCommand."); Type guard
static bool HasDbCommand(IDbConnection c) => c.CreateCommand() is System.Data.Common.DbCommand;
Try / catch
try { await cnn.QueryAsync<T>(sql); }
catch (InvalidOperationException ex) when (ex.Message.Contains("returns a DbCommand"))
{ /* use a provider whose command derives from DbCommand */ } Prevention
- Avoid mocking the raw IDb* interfaces for async tests; use DbConnection/DbCommand or a real provider.
- Confirm legacy providers derive their command types from System.Data.Common.DbCommand before using async APIs.
When it happens
Trigger: Using an async Dapper API with a connection whose CreateCommand() returns a custom IDbCommand that is not a DbCommand (legacy provider or a mock IDbConnection.CreateCommand returning a stub IDbCommand).
Common situations: Unit tests mocking IDbConnection.CreateCommand to return a non-DbCommand stub; a legacy ADO.NET provider that does not derive its command from DbConnection/DbCommand.
Related errors
- Async operations require use of a DbConnection or an already
- type
- you must provide at least one type to deserialize
- type
AI-assisted analysis of DapperLib/Dapper@72a54c475f (2026-08-13).
Data as JSON: /api/errors/95ef9b5e80f4be0f.
Report an issue: GitHub.