dotnet/efcore · error · InvalidOperationException

The current migration SQL generator

Error message

The current migration SQL generator '{sqlGeneratorType}' is unable to generate SQL for operations of type '{operationType}'.

What it means

MigrationsSqlGenerator.Generate(MigrationOperation, ...) dispatches by operation type via a prebuilt GenerateActions table keyed on the runtime operation type. If no entry exists for operation.GetType() (i.e. no Generate overload was registered for that type), it throws UnknownOperation. This usually means a custom MigrationOperation subclass was emitted that neither the base nor the provider generator knows how to render.

Solutions

  1. Subclass the provider's MigrationsSqlGenerator and add a Generate(YourCustomOperation, IModel, MigrationCommandListBuilder) overload, then register it via options.Use[Provider](b => b.MigrationsAssembly(...).ReplaceService<IMigrationsSqlGenerator, YourGenerator>()).
  2. If using a stock operation type, upgrade the database provider package to a version that implements its Generate method.
  3. Avoid emitting the unsupported operation; replace it with one the provider already supports.

Example fix

// before — custom operation throws at SQL generation
public class CreateViewOperation : MigrationOperation { /* ... */ }

// after — teach the generator to handle it
public class MySqlGenerator : SqlServerMigrationsSqlGenerator
{
    public MySqlGenerator(MigrationsSqlGeneratorDependencies deps)
        : base(deps) { }

    protected override void Generate(
        CreateViewOperation op, IModel? model, MigrationCommandListBuilder builder)
    {
        builder.AppendLine("CREATE VIEW ")
               .Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(op.Name))
               .AppendLine(" AS ").AppendLines(op.Body)
               .EndCommand();
    }
}

// register
options.UseSqlServer(conn,
    sql => sql.ReplaceService<IMigrationsSqlGenerator, MySqlGenerator>());
Defensive patterns

Strategy: type-guard

Validate before calling

// Before generating SQL, confirm the generator can handle each operation type
var gen = db.GetService<IMigrationsSqlGenerator>();
var handled = db.GetService<Type>() // placeholder — see typeGuard for runtime check
    is not null;

Type guard

// Check that a Generate method exists for the custom operation type
static bool GeneratorSupports(IMigrationsSqlGenerator gen, Type opType)
{
    return gen.GetType().GetMethods(BindingFlags.Instance |
            BindingFlags.NonPublic | BindingFlags.Public)
        .Any(m => m.Name == "Generate"
               && m.GetParameters() is { Length: 3 } p
               && p[0].ParameterType.IsAssignableFrom(opType));
}

Prevention

When it happens

Trigger: A custom MigrationOperation subclass is added to a migration's operations but the provider's MigrationsSqlGenerator has no matching Generate overload; an operation from a newer EF version is fed to an older/unextended provider; a plugin/extension registers an operation without also extending the SQL generator.

Common situations: Custom operation authored for one provider (e.g. SQLite) then run against another (e.g. SQL Server) whose generator was not extended; EF Core upgrade introduced a new operation type that the provider package version predates.

Related errors


AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11). Data as JSON: /api/errors/ae215f25e9478a28. Report an issue: GitHub.

Appendix: source

Thrown at src/EFCore.Relational/Migrations/MigrationsSqlGenerator.cs:156

    /// </summary>
    /// <remarks>
    ///     This method uses a double-dispatch mechanism to call one of the 'Generate' methods that are
    ///     specific to a certain subtype of <see cref="MigrationOperation" />. Typically database providers
    ///     will override these specific methods rather than this method. However, providers can override
    ///     this methods to handle provider-specific operations.
    /// </remarks>
    /// <param name="operation">The operation.</param>
    /// <param name="model">The target model which may be <see langword="null" /> if the operations exist without a model.</param>
    /// <param name="builder">The command builder to use to build the commands.</param>
    protected virtual void Generate(
        MigrationOperation operation,
        IModel? model,
        MigrationCommandListBuilder builder)
    {
        var operationType = operation.GetType();
        if (!GenerateActions.TryGetValue(operationType, out var generateAction))
        {
            throw new InvalidOperationException(RelationalStrings.UnknownOperation(GetType().ShortDisplayName(), operationType));
        }

        generateAction(this, operation, model, builder);
    }

    /// <summary>
    ///     Builds commands for the given <see cref="AddColumnOperation" /> by making calls on the given
    ///     <see cref="MigrationCommandListBuilder" />.
    /// </summary>
    /// <param name="operation">The operation.</param>
    /// <param name="model">The target model which may be <see langword="null" /> if the operations exist without a model.</param>
    /// <param name="builder">The command builder to use to build the commands.</param>
    /// <param name="terminate">Indicates whether or not to terminate the command after generating SQL for the operation.</param>
    protected virtual void Generate(
        AddColumnOperation operation,
        IModel? model,
        MigrationCommandListBuilder builder,
        bool terminate = true)

View on GitHub (pinned to 3a2006ef56)