HangfireIO/Hangfire · error · MissingMemberException

Property '{type.FullName}.Transaction' not found.

Error message

Property '{type.FullName}.Transaction' not found.

What it means

MissingMemberException thrown while compiling the setter for the Transaction property on SqlCommandSet: reflection found no Transaction property (public or non-public) on the resolved batch-command type. Same root cause family as the Connection/BatchCommand errors — the provider's internal surface changed.

Source

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

                    }

                    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));
                    var transactionProperty = type.GetProperty("Transaction", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingMemberException($"Property '{type.FullName}.Transaction' not found.");
                    return Expression.Lambda<Action<object, DbTransaction>>(Expression.Assign(Expression.Property(converted, transactionProperty), Expression.Convert(transactionParameter, transactionProperty.PropertyType)), p, transactionParameter).Compile();
                });
                var batchCommandProperty = BatchCommandProperty.GetOrAdd(sqlCommandSetType, static type =>
                {
                    return type.GetProperty("BatchCommand", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingMemberException($"Property '{type.FullName}.BatchCommand' not found.");
                });
                _getBatchCommand = GetBatchCommand.GetOrAdd(sqlCommandSetType, type =>
                {
                    var p = Expression.Parameter(typeof(object));
                    var converted = Expression.Convert(p, type);
                    return Expression.Lambda<Func<object, DbCommand>>(Expression.Property(converted, batchCommandProperty), p).Compile();
                });
                _appendMethod = AppendMethod.GetOrAdd(sqlCommandSetType, type =>
                {
                    var p = Expression.Parameter(typeof(object));
                    var converted = Expression.Convert(p, type);
                    var batchCommandParameter = Expression.Parameter(typeof(DbCommand));
                    return Expression.Lambda<Action<object, DbCommand>>(Expression.Call(converted, "Append", null, Expression.Convert(batchCommandParameter, batchCommandProperty.PropertyType)), p, batchCommandParameter).Compile();

View on GitHub (pinned to c236dd0f93)

Solutions

  1. Pin a SqlClient version documented as compatible with your Hangfire.SqlServer release.
  2. Disable trimming/obfuscation for the SqlClient assembly, or publish untrimmed.
  3. Upgrade Hangfire.SqlServer so its reflection targets match the provider's current layout.

Example fix

// before (trimming enabled, removes internal property metadata)
<PropertyGroup><PublishTrimmed>true</PublishTrimmed></PropertyGroup>

// after
<PropertyGroup><PublishTrimmed>false</PublishTrimmed></PropertyGroup>
Defensive patterns

Strategy: type-guard

Validate before calling

static bool HasTransactionProperty(Type sqlCommandSetType)
    => sqlCommandSetType.GetProperty("Transaction",
        BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) != null;

Type guard

static bool IsSqlCommandSetUsable(Type type) =>
    type.GetProperty("Transaction", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) != null;

Prevention

When it happens

Trigger: SqlClient assembly exposes a SqlCommandSet type but lacks a discoverable Transaction property; provider was trimmed, obfuscated, or is an incompatible version.

Common situations: Upgrading the SqlClient package without updating Hangfire.SqlServer; enabling IL trimming that removes 'unused' internal property metadata; mismatched provider major versions.

Related errors


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