Skip to content

Commit 1891f9d

Browse files
committed
Add more specific messages when pending model changes are detected.
Fixes #35133
1 parent bd7d0aa commit 1891f9d

File tree

10 files changed

+416
-65
lines changed

10 files changed

+416
-65
lines changed

src/EFCore.Relational/Diagnostics/RelationalEventId.cs

+14
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ private enum Id
8181
NonTransactionalMigrationOperationWarning,
8282
AcquiringMigrationLock,
8383
MigrationsUserTransactionWarning,
84+
ModelSnapshotNotFound,
8485

8586
// Query events
8687
QueryClientEvaluationWarning = CoreEventId.RelationalBaseId + 500,
@@ -778,6 +779,19 @@ private static EventId MakeMigrationsId(Id id)
778779
/// </remarks>
779780
public static readonly EventId MigrationsUserTransactionWarning = MakeMigrationsId(Id.MigrationsUserTransactionWarning);
780781

782+
/// <summary>
783+
/// Model snapshot was not found.
784+
/// </summary>
785+
/// <remarks>
786+
/// <para>
787+
/// This event is in the <see cref="DbLoggerCategory.Migrations" /> category.
788+
/// </para>
789+
/// <para>
790+
/// This event uses the <see cref="MigrationAssemblyEventData" /> payload when used with a <see cref="DiagnosticSource" />.
791+
/// </para>
792+
/// </remarks>
793+
public static readonly EventId ModelSnapshotNotFound = MakeMigrationsId(Id.ModelSnapshotNotFound);
794+
781795
private static readonly string _queryPrefix = DbLoggerCategory.Query.Name + ".";
782796

783797
private static EventId MakeQueryId(Id id)

src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs

+71
Original file line numberDiff line numberDiff line change
@@ -2343,6 +2343,77 @@ private static string PendingModelChanges(EventDefinitionBase definition, EventD
23432343
return d.GenerateMessage(p.ContextType.ShortDisplayName());
23442344
}
23452345

2346+
/// <summary>
2347+
/// Logs for the <see cref="RelationalEventId.PendingModelChangesWarning" /> event.
2348+
/// </summary>
2349+
/// <param name="diagnostics">The diagnostics logger to use.</param>
2350+
/// <param name="contextType">The <see cref="DbContext" /> type being used.</param>
2351+
public static void NonDeterministicModel(
2352+
this IDiagnosticsLogger<DbLoggerCategory.Migrations> diagnostics,
2353+
Type contextType)
2354+
{
2355+
var definition = RelationalResources.LogNonDeterministicModel(diagnostics);
2356+
2357+
if (diagnostics.ShouldLog(definition))
2358+
{
2359+
definition.Log(diagnostics, contextType.ShortDisplayName());
2360+
}
2361+
2362+
if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
2363+
{
2364+
var eventData = new DbContextTypeEventData(
2365+
definition,
2366+
NonDeterministicModel,
2367+
contextType);
2368+
2369+
diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
2370+
}
2371+
}
2372+
2373+
private static string NonDeterministicModel(EventDefinitionBase definition, EventData payload)
2374+
{
2375+
var d = (EventDefinition<string>)definition;
2376+
var p = (DbContextTypeEventData)payload;
2377+
return d.GenerateMessage(p.ContextType.ShortDisplayName());
2378+
}
2379+
2380+
/// <summary>
2381+
/// Logs for the <see cref="RelationalEventId.MigrationsNotFound" /> event.
2382+
/// </summary>
2383+
/// <param name="diagnostics">The diagnostics logger to use.</param>
2384+
/// <param name="migrator">The migrator.</param>
2385+
/// <param name="migrationsAssembly">The assembly in which migrations are stored.</param>
2386+
public static void ModelSnapshotNotFound(
2387+
this IDiagnosticsLogger<DbLoggerCategory.Migrations> diagnostics,
2388+
IMigrator migrator,
2389+
IMigrationsAssembly migrationsAssembly)
2390+
{
2391+
var definition = RelationalResources.LogNoModelSnapshotFound(diagnostics);
2392+
2393+
if (diagnostics.ShouldLog(definition))
2394+
{
2395+
definition.Log(diagnostics, migrationsAssembly.Assembly.GetName().Name!);
2396+
}
2397+
2398+
if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
2399+
{
2400+
var eventData = new MigrationAssemblyEventData(
2401+
definition,
2402+
ModelSnapshotNotFound,
2403+
migrator,
2404+
migrationsAssembly);
2405+
2406+
diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
2407+
}
2408+
}
2409+
2410+
private static string ModelSnapshotNotFound(EventDefinitionBase definition, EventData payload)
2411+
{
2412+
var d = (EventDefinition<string>)definition;
2413+
var p = (MigrationAssemblyEventData)payload;
2414+
return d.GenerateMessage(p.MigrationsAssembly.Assembly.GetName().Name!);
2415+
}
2416+
23462417
/// <summary>
23472418
/// Logs for the <see cref="RelationalEventId.NonTransactionalMigrationOperationWarning" /> event.
23482419
/// </summary>

src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs

+19-1
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ public abstract class RelationalLoggingDefinitions : LoggingDefinitions
374374
/// doing so can result in application failures when updating to a new Entity Framework Core release.
375375
/// </summary>
376376
[EntityFrameworkInternal]
377-
public EventDefinitionBase? LogMigrationsUserTransactionWarning;
377+
public EventDefinitionBase? LogMigrationsUserTransaction;
378378

379379
/// <summary>
380380
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -673,6 +673,24 @@ public abstract class RelationalLoggingDefinitions : LoggingDefinitions
673673
[EntityFrameworkInternal]
674674
public EventDefinitionBase? LogPendingModelChanges;
675675

676+
/// <summary>
677+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
678+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
679+
/// any release. You should only use it directly in your code with extreme caution and knowing that
680+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
681+
/// </summary>
682+
[EntityFrameworkInternal]
683+
public EventDefinitionBase? LogNonDeterministicModel;
684+
685+
/// <summary>
686+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
687+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
688+
/// any release. You should only use it directly in your code with extreme caution and knowing that
689+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
690+
/// </summary>
691+
[EntityFrameworkInternal]
692+
public EventDefinitionBase? LogNoModelSnapshotFound;
693+
676694
/// <summary>
677695
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
678696
/// the same compatibility standards as public APIs. It may be changed or removed without notice in

src/EFCore.Relational/Migrations/Internal/Migrator.cs

+45-40
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Transactions;
55
using Microsoft.EntityFrameworkCore.Diagnostics.Internal;
6+
using Microsoft.EntityFrameworkCore.Metadata.Internal;
67

78
namespace Microsoft.EntityFrameworkCore.Migrations.Internal;
89

@@ -93,24 +94,7 @@ public Migrator(
9394
public virtual void Migrate(string? targetMigration)
9495
{
9596
var useTransaction = _connection.CurrentTransaction is null;
96-
if (!useTransaction
97-
&& _executionStrategy.RetriesOnFailure)
98-
{
99-
throw new NotSupportedException(RelationalStrings.TransactionSuppressedMigrationInUserTransaction);
100-
}
101-
102-
if (RelationalResources.LogPendingModelChanges(_logger).WarningBehavior != WarningBehavior.Ignore
103-
&& HasPendingModelChanges())
104-
{
105-
_logger.PendingModelChangesWarning(_currentContext.Context.GetType());
106-
}
107-
108-
if (!useTransaction)
109-
{
110-
_logger.MigrationsUserTransactionWarning();
111-
}
112-
113-
_logger.MigrateUsingConnection(this, _connection);
97+
ValidateMigrations(useTransaction);
11498

11599
using var transactionScope = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled);
116100

@@ -235,24 +219,7 @@ public virtual async Task MigrateAsync(
235219
CancellationToken cancellationToken = default)
236220
{
237221
var useTransaction = _connection.CurrentTransaction is null;
238-
if (!useTransaction
239-
&& _executionStrategy.RetriesOnFailure)
240-
{
241-
throw new NotSupportedException(RelationalStrings.TransactionSuppressedMigrationInUserTransaction);
242-
}
243-
244-
if (RelationalResources.LogPendingModelChanges(_logger).WarningBehavior != WarningBehavior.Ignore
245-
&& HasPendingModelChanges())
246-
{
247-
_logger.PendingModelChangesWarning(_currentContext.Context.GetType());
248-
}
249-
250-
if (!useTransaction)
251-
{
252-
_logger.MigrationsUserTransactionWarning();
253-
}
254-
255-
_logger.MigrateUsingConnection(this, _connection);
222+
ValidateMigrations(useTransaction);
256223

257224
using var transactionScope = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled);
258225

@@ -382,6 +349,48 @@ await _migrationCommandExecutor.ExecuteNonQueryAsync(
382349
}
383350
}
384351

352+
private void ValidateMigrations(bool useTransaction)
353+
{
354+
if (!useTransaction
355+
&& _executionStrategy.RetriesOnFailure)
356+
{
357+
throw new NotSupportedException(RelationalStrings.TransactionSuppressedMigrationInUserTransaction);
358+
}
359+
360+
if (_migrationsAssembly.Migrations.Count == 0)
361+
{
362+
_logger.MigrationsNotFound(this, _migrationsAssembly);
363+
}
364+
else if (_migrationsAssembly.ModelSnapshot == null)
365+
{
366+
_logger.ModelSnapshotNotFound(this, _migrationsAssembly);
367+
}
368+
else if (RelationalResources.LogPendingModelChanges(_logger).WarningBehavior != WarningBehavior.Ignore
369+
&& HasPendingModelChanges())
370+
{
371+
var modelSource = (ModelSource)_currentContext.Context.GetService<IModelSource>();
372+
#pragma warning disable EF1001 // Internal EF Core API usage.
373+
var newDesignTimeModel = modelSource.CreateModel(
374+
_currentContext.Context, _currentContext.Context.GetService<ModelCreationDependencies>(), designTime: true);
375+
#pragma warning restore EF1001 // Internal EF Core API usage.
376+
if (_migrationsModelDiffer.HasDifferences(newDesignTimeModel.GetRelationalModel(), _designTimeModel.Model.GetRelationalModel()))
377+
{
378+
_logger.NonDeterministicModel(_currentContext.Context.GetType());
379+
}
380+
else
381+
{
382+
_logger.PendingModelChangesWarning(_currentContext.Context.GetType());
383+
}
384+
}
385+
386+
if (!useTransaction)
387+
{
388+
_logger.MigrationsUserTransactionWarning();
389+
}
390+
391+
_logger.MigrateUsingConnection(this, _connection);
392+
}
393+
385394
private IEnumerable<(string, Func<IReadOnlyList<MigrationCommand>>)> GetMigrationCommandLists(MigratorData parameters)
386395
{
387396
var migrationsToApply = parameters.AppliedMigrations;
@@ -449,10 +458,6 @@ protected virtual void PopulateMigrations(
449458
var appliedMigrations = new Dictionary<string, TypeInfo>();
450459
var unappliedMigrations = new Dictionary<string, TypeInfo>();
451460
var appliedMigrationEntrySet = new HashSet<string>(appliedMigrationEntries, StringComparer.OrdinalIgnoreCase);
452-
if (_migrationsAssembly.Migrations.Count == 0)
453-
{
454-
_logger.MigrationsNotFound(this, _migrationsAssembly);
455-
}
456461

457462
foreach (var (key, typeInfo) in _migrationsAssembly.Migrations)
458463
{

src/EFCore.Relational/Properties/RelationalStrings.Designer.cs

+56-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)