Skip to content

Commit fb8ee97

Browse files
rojiCopilot
andcommitted
Filter out full-text catalog operations from history table creation
When the migration history table is created, the model differ generates AlterDatabaseOperation with full-text catalog annotations. The SQL generator then emits CREATE FULLTEXT CATALOG too early, causing the later actual migration to fail. Filter out SqlServerAnnotationNames.FullTextCatalogs from AlterDatabaseOperation in SqlServerHistoryRepository.GetCreateCommands(), following the same approach as npgsql/efcore.pg#3713. This is a workaround for #34991. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 713aaf0 commit fb8ee97

3 files changed

Lines changed: 60 additions & 4 deletions

File tree

src/EFCore.Relational/Migrations/HistoryRepository.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,14 @@ protected virtual string MigrationIdColumnName
8585
.FindProperty(nameof(HistoryRow.MigrationId))!
8686
.GetColumnName();
8787

88-
private IModel EnsureModel()
88+
/// <summary>
89+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
90+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
91+
/// any release. You should only use it directly in your code with extreme caution and knowing that
92+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
93+
/// </summary>
94+
[EntityFrameworkInternal]
95+
protected IModel EnsureModel()
8996
{
9097
if (_model == null)
9198
{

src/EFCore.SqlServer/Migrations/Internal/SqlServerHistoryRepository.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Text;
5+
using Microsoft.EntityFrameworkCore.SqlServer.Metadata.Internal;
56

67
namespace Microsoft.EntityFrameworkCore.SqlServer.Migrations.Internal;
78

@@ -24,6 +25,36 @@ public SqlServerHistoryRepository(HistoryRepositoryDependencies dependencies)
2425
{
2526
}
2627

28+
/// <summary>
29+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
30+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
31+
/// any release. You should only use it directly in your code with extreme caution and knowing that
32+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
33+
/// </summary>
34+
protected override IReadOnlyList<MigrationCommand> GetCreateCommands()
35+
{
36+
// TODO: This is a hack around https://github.com/dotnet/efcore/issues/34991: provider-specific conventions may add
37+
// database-level annotations (e.g. full-text catalogs) to the model, and the default EF logic causes them to be created
38+
// at this point, when the history table is being created. This is too early, and causes the later actual migration to fail.
39+
// So we filter out full-text catalog annotations from AlterDatabaseOperation.
40+
// This follows the same approach as the Npgsql provider (npgsql/efcore.pg#3713).
41+
#pragma warning disable EF1001 // Internal EF Core API usage.
42+
var model = EnsureModel();
43+
#pragma warning restore EF1001 // Internal EF Core API usage.
44+
45+
var operations = Dependencies.ModelDiffer.GetDifferences(null, model.GetRelationalModel());
46+
47+
foreach (var operation in operations)
48+
{
49+
if (operation is AlterDatabaseOperation alterDatabaseOperation)
50+
{
51+
alterDatabaseOperation.RemoveAnnotation(SqlServerAnnotationNames.FullTextCatalogs);
52+
}
53+
}
54+
55+
return Dependencies.MigrationsSqlGenerator.Generate(operations, model);
56+
}
57+
2758
/// <summary>
2859
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
2960
/// the same compatibility standards as public APIs. It may be changed or removed without notice in

test/EFCore.SqlServer.Tests/Migrations/SqlServerHistoryRepositoryTest.cs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,22 @@ [ProductVersion] nvarchar(32) NOT NULL,
2121
CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId])
2222
);
2323
24+
""", sql, ignoreLineEndingDifferences: true);
25+
}
26+
27+
[ConditionalFact]
28+
public void GetCreateScript_works_with_full_text_catalog()
29+
{
30+
var sql = CreateHistoryRepository(configureModel: b => b.HasFullTextCatalog("MyCatalog")).GetCreateScript();
31+
32+
Assert.Equal(
33+
"""
34+
CREATE TABLE [__EFMigrationsHistory] (
35+
[MigrationId] nvarchar(150) NOT NULL,
36+
[ProductVersion] nvarchar(32) NOT NULL,
37+
CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId])
38+
);
39+
2440
""", sql, ignoreLineEndingDifferences: true);
2541
}
2642

@@ -149,17 +165,18 @@ public void GetEndIfScript_works()
149165
""", sql, ignoreLineEndingDifferences: true);
150166
}
151167

152-
private static IHistoryRepository CreateHistoryRepository(string schema = null)
168+
private static IHistoryRepository CreateHistoryRepository(string schema = null, Action<ModelBuilder> configureModel = null)
153169
=> new TestDbContext(
154170
new DbContextOptionsBuilder()
155171
.UseInternalServiceProvider(SqlServerTestHelpers.Instance.CreateServiceProvider())
156172
.UseSqlServer(
157173
new SqlConnection("Database=DummyDatabase"),
158174
b => b.MigrationsHistoryTable(HistoryRepository.DefaultTableName, schema))
159-
.Options)
175+
.Options,
176+
configureModel)
160177
.GetService<IHistoryRepository>();
161178

162-
private class TestDbContext(DbContextOptions options) : DbContext(options)
179+
private class TestDbContext(DbContextOptions options, Action<ModelBuilder> configureModel = null) : DbContext(options)
163180
{
164181
public DbSet<Blog> Blogs { get; set; }
165182

@@ -169,6 +186,7 @@ public IQueryable<TableFunction> TableFunction()
169186

170187
protected override void OnModelCreating(ModelBuilder modelBuilder)
171188
{
189+
configureModel?.Invoke(modelBuilder);
172190
}
173191
}
174192

0 commit comments

Comments
 (0)