HangfireIO/Hangfire · error · TypeLoadException

Could not load type 'SqlCommandSet' from assembly '{sqlClien

Error message

Could not load type 'SqlCommandSet' from assembly '{sqlClientAssembly}'.

What it means

TypeLoadException thrown by SqlCommandSet's type loader when no public/internal type named 'SqlCommandSet' can be found in the resolved sqlClient assembly via GetTypes().FirstOrDefault. This means the provider assembly either does not expose batch-command infrastructure at all or its layout differs from what Hangfire expects.

Source

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

                sqlCommandSetType = SqlCommandSetType.GetOrAdd(connection.GetType().GetTypeInfo().Assembly, static sqlClientAssembly =>
                {
                    var assemblyName = sqlClientAssembly.GetName();
                    var version = assemblyName.Version;

                    if (assemblyName.Name == "System.Data.SqlClient" && Version.Parse("4.0.0.0") < version && version < Version.Parse("4.6.0.0"))
                    {
                        // .NET Core version of the System.Data.SqlClient package below 4.7.0 (which
                        // has assembly version 4.6.0.0) doesn't properly implement the SqlCommandSet
                        // class, throwing the following exception in run-time:
                        // ArgumentException: Specified parameter name 'Parameter1' is not valid.
                        // GitHub Issue: https://github.com/dotnet/corefx/issues/29391
                        throw new NotSupportedException(".NET Core version of the System.Data.SqlClient package below 4.7.0 (which has assembly version 4.6.0.0) doesn't properly implement the SqlCommandSet class.");
                    }

                    var type = sqlClientAssembly.GetTypes().FirstOrDefault(static x => x.Name == "SqlCommandSet");
                    if (type == null)
                    {
                        throw new TypeLoadException($"Could not load type 'SqlCommandSet' from assembly '{sqlClientAssembly}'.");
                    }

                    return type;
                });

                _setConnection = SetConnection.GetOrAdd(sqlCommandSetType, static type =>
                {
                    var p = Expression.Parameter(typeof(object));
                    var converted = Expression.Convert(p, type);
                    var connectionParameter = Expression.Parameter(typeof(DbConnection));
                    var connectionProperty = type.GetProperty("Connection", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingMemberException($"Property '{type.FullName}.Connection' not found.");
                    return Expression.Lambda<Action<object, DbConnection>>(Expression.Assign(Expression.Property(converted, connectionProperty), Expression.Convert(connectionParameter, connectionProperty.PropertyType)), p, connectionParameter).Compile();
                });
                _setTransaction = SetTransaction.GetOrAdd(sqlCommandSetType, static type =>
                {
                    var p = Expression.Parameter(typeof(object));
                    var converted = Expression.Convert(p, type);
                    var transactionParameter = Expression.Parameter(typeof(DbTransaction));

View on GitHub (pinned to c236dd0f93)

Solutions

  1. Use a supported Microsoft.Data.SqlClient / System.Data.SqlClient version that exposes the SqlCommandSet type Hangfire expects.
  2. If using a custom/in-memory DbConnection in tests, disable batch inserts or provide a real provider connection.
  3. Update Hangfire.SqlServer to a version compatible with your provider's type layout.

Example fix

// before (test using a fake DbConnection)
using var conn = new FakeDbConnection();

// after (use the real provider)
using var conn = new SqlConnection(connectionString);
Defensive patterns

Strategy: type-guard

Validate before calling

static bool HasSqlCommandSetType(DbConnection connection)
    => connection.GetType().Assembly.GetTypes().Any(t => t.Name == "SqlCommandSet");

Type guard

static bool SupportsBatchCommands(DbConnection connection) =>
    connection.GetType().Assembly.GetTypes().Any(static t => t.Name == "SqlCommandSet");

Prevention

When it happens

Trigger: Pointing Hangfire.SqlServer at a connection whose underlying provider assembly has no SqlCommandSet type (e.g. Microsoft.Data.SqlClient on a platform/version where the internal type was renamed, or a third-party provider).

Common situations: Switching from System.Data.SqlClient to Microsoft.Data.SqlClient where the batch type isn't located; using a provider stub or mocked DbConnection in tests; provider version that reorganized internal types.

Related errors


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