HangfireIO/Hangfire · error · NotSupportedException

SqlCommandSet for is not supported, use regular commands…

Error message

SqlCommandSet for {connection.GetType().FullName} is not supported, use regular commands instead

What it means

NotSupportedException thrown by the SqlCommandSet constructor's catch-all: any reflection/setup exception (TypeLoadException, MissingMemberException, etc.) while probing the provider's batch type is wrapped here with the connection's full type name, directing you to fall back to regular (non-batched) commands. It is the umbrella error for 'this provider can't be used for batched inserts'.

Solutions

  1. Read the InnerException to find the exact reflection failure (type missing, property missing, etc.) and address that specifically.
  2. Switch to a supported Microsoft.Data.SqlClient / System.Data.SqlClient version and disable trimming.
  3. If the provider is genuinely unsupported, configure Hangfire to use regular (non-batch) command execution if such an option is available, or replace the test fake with a real provider connection.

Example fix

// before (fake connection in integration test)
var conn = new FakeDbConnection();
using var batch = new SqlCommandSet(conn); // -> NotSupportedException

// after
var conn = new SqlConnection(realConnectionString);
conn.Open();
using var batch = new SqlCommandSet(conn);
Defensive patterns

Strategy: try-catch

Validate before calling

static bool ProviderSupportsBatch(DbConnection conn)
{
    try
    {
        var t = conn.GetType().Assembly.GetTypes().FirstOrDefault(x => x.Name == "SqlCommandSet");
        if (t == null) return false;
        foreach (var p in new[] { "Connection", "Transaction", "BatchCommand" })
            if (t.GetProperty(p, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) == null)
                return false;
        return true;
    }
    catch { return false; }
}

Try / catch

try
{
    using var batch = new SqlCommandSet(connection);
}
catch (NotSupportedException ex)
{
    logger.Warn(ex.InnerException, "Falling back to regular commands for {Conn}", connection.GetType().FullName);
    // execute commands individually
}

Prevention

When it happens

Trigger: Constructing SqlCommandSet for a DbConnection whose provider fails any of the reflection steps (type load, property binding, delegate compilation); common with an unsupported/trimmed/incompatible provider.

Common situations: Incompatible SqlClient version, trimmed publish, a mocked/fake DbConnection in tests, or a third-party provider that does not mirror SqlClient's internal SqlCommandSet.

Related errors


AI-assisted analysis of HangfireIO/Hangfire@c236dd0f93 (2026-08-13). Data as JSON: /api/errors/9c8ad98b670c7a83. Report an issue: GitHub.

Appendix: source

Thrown at src/Hangfire.SqlServer/SqlCommandSet.cs:129

                    var p = Expression.Parameter(typeof(object));
                    var converted = Expression.Convert(p, type);
                    return Expression.Lambda<Func<object, int>>(Expression.Call(converted, "ExecuteNonQuery", null), p).Compile();
                });
                _disposeMethod = DisposeMethod.GetOrAdd(sqlCommandSetType, static type =>
                {
                    var p = Expression.Parameter(typeof(object));
                    var converted = Expression.Convert(p, type);
                    return Expression.Lambda<Action<object>>(Expression.Call(converted, "Dispose", null), p).Compile();
                });
                _constructor = SqlCommandSetConstructor.GetOrAdd(sqlCommandSetType, static type =>
                {
                    var ctor = Expression.New(type);
                    return Expression.Lambda<Func<object>>(ctor).Compile();
                });
            }
            catch (Exception exception) when (exception.IsCatchableExceptionType())
            {
                throw new NotSupportedException($"SqlCommandSet for {connection.GetType().FullName} is not supported, use regular commands instead", exception);
            }

            _instance = _constructor();
        }

        public DbConnection Connection
        {
            set => _setConnection(_instance, value);
        }

        public DbTransaction Transaction
        {
            set => _setTransaction(_instance, value);
        }

        public DbCommand BatchCommand => _getBatchCommand(_instance);
        public int CommandCount { get; private set; }

View on GitHub (pinned to c236dd0f93)