Skip to content

Commit 253f08d

Browse files
Log warning and skip discovery for compiled models built for a different provider (#37840)
Fixes #35517 Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
1 parent a6f77af commit 253f08d

63 files changed

Lines changed: 403 additions & 53 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/EFCore.Design/Design/Internal/DbContextOperations.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ private IReadOnlyList<string> ScaffoldCompiledModel(
259259
outputDir = Path.GetFullPath(Path.Combine(_projectDir, outputDir));
260260

261261
var scaffolder = services.GetRequiredService<ICompiledModelScaffolder>();
262+
var databaseProvider = context.GetService<IDatabaseProvider>();
262263

263264
var finalModelNamespace = modelNamespace ?? GetNamespaceFromOutputPath(outputDir) ?? "";
264265

@@ -273,6 +274,7 @@ private IReadOnlyList<string> ScaffoldCompiledModel(
273274
UseNullableReferenceTypes = _nullable,
274275
Suffix = suffix,
275276
ForNativeAot = nativeAot,
277+
ProviderName = databaseProvider.Name,
276278
GeneratedFileNames = generatedFileNames
277279
});
278280

src/EFCore.Design/Scaffolding/CompiledModelCodeGenerationOptions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ public class CompiledModelCodeGenerationOptions
4444
/// <value> A value indicating whether the generated code should be compatible with NativeAOT. </value>
4545
public virtual bool ForNativeAot { get; set; }
4646

47+
/// <summary>
48+
/// Gets or sets the database provider name to embed in the compiled model.
49+
/// </summary>
50+
/// <value> The database provider name. </value>
51+
public virtual string? ProviderName { get; set; }
52+
4753
/// <summary>
4854
/// Gets or sets the set of file names generated so far.
4955
/// </summary>

src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public virtual IReadOnlyCollection<ScaffoldedFile> GenerateModel(
6666
var nullable = false;
6767
var scaffoldedFiles = new List<ScaffoldedFile>();
6868

69-
var assemblyAttributesCode = CreateAssemblyAttributes(options.ModelNamespace, options.ContextType, nullable);
69+
var assemblyAttributesCode = CreateAssemblyAttributes(options.ModelNamespace, options.ContextType, options.ProviderName, nullable);
7070
var assemblyInfoFileName = UniquifyFileName(options.ContextType.ShortDisplayName() + AssemblyAttributesSuffix, options);
7171
scaffoldedFiles.Add(new ScaffoldedFile(assemblyInfoFileName, assemblyAttributesCode));
7272

@@ -174,6 +174,7 @@ private static string GenerateHeader(SortedSet<string> namespaces, string curren
174174
private string CreateAssemblyAttributes(
175175
string @namespace,
176176
Type contextType,
177+
string? providerName,
177178
bool nullable)
178179
{
179180
var mainBuilder = new IndentedStringBuilder();
@@ -183,7 +184,15 @@ private string CreateAssemblyAttributes(
183184

184185
mainBuilder
185186
.Append("[assembly: DbContextModel(typeof(").Append(_code.Reference(contextType))
186-
.Append("), typeof(").Append(GetModelClassName(contextType)).AppendLine("))]");
187+
.Append("), typeof(").Append(GetModelClassName(contextType)).Append(")");
188+
189+
if (providerName != null)
190+
{
191+
mainBuilder
192+
.Append(", ProviderName = ").Append(_code.Literal(providerName));
193+
}
194+
195+
mainBuilder.AppendLine(")]");
187196

188197
return GenerateHeader(namespaces, currentNamespace: "", nullable) + mainBuilder;
189198
}

src/EFCore/Diagnostics/CoreEventId.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ private enum Id
9090
ServiceProviderDebugInfo,
9191
RedundantAddServicesCallWarning,
9292
OldModelVersionWarning,
93+
CompiledModelProviderMismatchWarning,
9394

9495
// Model and ModelValidation events
9596
ShadowPropertyCreated = CoreBaseId + 600,
@@ -491,6 +492,19 @@ private static EventId MakeInfraId(Id id)
491492
/// </remarks>
492493
public static readonly EventId OldModelVersionWarning = MakeInfraId(Id.OldModelVersionWarning);
493494

495+
/// <summary>
496+
/// A compiled model was found but was built for a different database provider.
497+
/// </summary>
498+
/// <remarks>
499+
/// <para>
500+
/// This event is in the <see cref="DbLoggerCategory.Infrastructure" /> category.
501+
/// </para>
502+
/// <para>
503+
/// This event uses the <see cref="ProviderMismatchEventData" /> payload when used with a <see cref="DiagnosticSource" />.
504+
/// </para>
505+
/// </remarks>
506+
public static readonly EventId CompiledModelProviderMismatchWarning = MakeInfraId(Id.CompiledModelProviderMismatchWarning);
507+
494508
private static readonly string _modelPrefix = DbLoggerCategory.Model.Name + ".";
495509

496510
private static EventId MakeModelId(Id id)

src/EFCore/Diagnostics/CoreLoggerExtensions.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,46 @@ private static string OldModelVersion(EventDefinitionBase definition, EventData
230230
ProductInfo.GetVersion());
231231
}
232232

233+
/// <summary>
234+
/// Logs for the <see cref="CoreEventId.CompiledModelProviderMismatchWarning" /> event.
235+
/// </summary>
236+
/// <param name="diagnostics">The diagnostics logger to use.</param>
237+
/// <param name="compiledProviderName">The provider name stored in the compiled model.</param>
238+
/// <param name="currentProviderName">The provider name currently configured.</param>
239+
public static void CompiledModelProviderMismatchWarning(
240+
this IDiagnosticsLogger<DbLoggerCategory.Infrastructure> diagnostics,
241+
string compiledProviderName,
242+
string currentProviderName)
243+
{
244+
var definition = CoreResources.LogCompiledModelProviderMismatch(diagnostics);
245+
246+
if (diagnostics.ShouldLog(definition))
247+
{
248+
definition.Log(
249+
diagnostics,
250+
compiledProviderName,
251+
currentProviderName);
252+
}
253+
254+
if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
255+
{
256+
var eventData = new ProviderMismatchEventData(
257+
definition,
258+
CompiledModelProviderMismatch,
259+
compiledProviderName,
260+
currentProviderName);
261+
262+
diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
263+
}
264+
}
265+
266+
private static string CompiledModelProviderMismatch(EventDefinitionBase definition, EventData payload)
267+
{
268+
var d = (EventDefinition<string, string>)definition;
269+
var p = (ProviderMismatchEventData)payload;
270+
return d.GenerateMessage(p.CompiledProviderName, p.CurrentProviderName);
271+
}
272+
233273
/// <summary>
234274
/// Logs for the <see cref="CoreEventId.OptimisticConcurrencyException" /> event.
235275
/// </summary>

src/EFCore/Diagnostics/LoggingDefinitions.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,15 @@ public abstract class LoggingDefinitions
106106
[EntityFrameworkInternal]
107107
public EventDefinitionBase? LogOldModelVersion;
108108

109+
/// <summary>
110+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
111+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
112+
/// any release. You should only use it directly in your code with extreme caution and knowing that
113+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
114+
/// </summary>
115+
[EntityFrameworkInternal]
116+
public EventDefinitionBase? LogCompiledModelProviderMismatch;
117+
109118
/// <summary>
110119
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
111120
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.EntityFrameworkCore.Diagnostics;
5+
6+
/// <summary>
7+
/// A <see cref="DiagnosticSource" /> event payload class for provider mismatch warnings.
8+
/// </summary>
9+
/// <remarks>
10+
/// See <see href="https://aka.ms/efcore-docs-diagnostics">Logging, events, and diagnostics</see> for more information and examples.
11+
/// </remarks>
12+
public class ProviderMismatchEventData : EventData
13+
{
14+
/// <summary>
15+
/// Constructs the event payload.
16+
/// </summary>
17+
/// <param name="eventDefinition">The event definition.</param>
18+
/// <param name="messageGenerator">A delegate that generates a log message for this event.</param>
19+
/// <param name="compiledProviderName">The provider name stored with the model.</param>
20+
/// <param name="currentProviderName">The provider name currently configured.</param>
21+
public ProviderMismatchEventData(
22+
EventDefinitionBase eventDefinition,
23+
Func<EventDefinitionBase, EventData, string> messageGenerator,
24+
string compiledProviderName,
25+
string currentProviderName)
26+
: base(eventDefinition, messageGenerator)
27+
{
28+
CompiledProviderName = compiledProviderName;
29+
CurrentProviderName = currentProviderName;
30+
}
31+
32+
/// <summary>
33+
/// The provider name stored with the model.
34+
/// </summary>
35+
public virtual string CompiledProviderName { get; }
36+
37+
/// <summary>
38+
/// The provider name currently configured.
39+
/// </summary>
40+
public virtual string CurrentProviderName { get; }
41+
}

src/EFCore/Infrastructure/DbContextModelAttribute.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,9 @@ public DbContextModelAttribute(
4141
/// Gets the compiled model.
4242
/// </summary>
4343
public Type ModelType { get; }
44+
45+
/// <summary>
46+
/// Gets the database provider name used to build the compiled model.
47+
/// </summary>
48+
public string? ProviderName { get; set; }
4449
}

src/EFCore/Internal/DbContextServices.cs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,22 @@ private IModel CreateModel(bool designTime)
6767
_inOnModelCreating = true;
6868

6969
var dependencies = _scopedProvider!.GetRequiredService<ModelCreationDependencies>();
70-
var modelFromOptions = CoreOptions?.Model ?? FindCompiledModel(_currentContext!.Context.GetType());
70+
71+
string? mismatchedProviderName = null;
72+
var modelFromOptions = CoreOptions?.Model;
73+
if (modelFromOptions == null)
74+
{
75+
var providers = _scopedProvider!.GetService<IEnumerable<IDatabaseProvider>>()?.ToList();
76+
var providerName = providers is [var provider] ? provider.Name : null;
77+
78+
modelFromOptions = FindCompiledModel(_currentContext!.Context.GetType(), providerName, out mismatchedProviderName);
79+
80+
if (mismatchedProviderName != null)
81+
{
82+
var logger = _scopedProvider!.GetRequiredService<IDiagnosticsLogger<DbLoggerCategory.Infrastructure>>();
83+
logger.CompiledModelProviderMismatchWarning(mismatchedProviderName, providerName!);
84+
}
85+
}
7186

7287
var modelVersion = modelFromOptions?.GetProductVersion();
7388
if (modelVersion != null)
@@ -105,17 +120,27 @@ private IModel CreateModel(bool designTime)
105120
_inOnModelCreating = false;
106121
}
107122

108-
static IModel? FindCompiledModel(Type contextType)
123+
static IModel? FindCompiledModel(Type contextType, string? providerName, out string? mismatchedProviderName)
109124
{
125+
mismatchedProviderName = null;
110126
var contextAssembly = contextType.Assembly;
111127
IModel? model = null;
128+
string? firstMismatchedProvider = null;
112129
foreach (var modelAttribute in contextAssembly.GetCustomAttributes<DbContextModelAttribute>())
113130
{
114131
if (modelAttribute.ContextType != contextType)
115132
{
116133
continue;
117134
}
118135

136+
if (modelAttribute.ProviderName != null
137+
&& providerName != null
138+
&& modelAttribute.ProviderName != providerName)
139+
{
140+
firstMismatchedProvider ??= modelAttribute.ProviderName;
141+
continue;
142+
}
143+
119144
var modelType = modelAttribute.ModelType;
120145

121146
var instanceProperty = modelType.GetProperty("Instance", BindingFlags.Public | BindingFlags.Static);
@@ -135,6 +160,11 @@ private IModel CreateModel(bool designTime)
135160
model = (IModel)instanceProperty.GetValue(null)!;
136161
}
137162

163+
if (model == null)
164+
{
165+
mismatchedProviderName = firstMismatchedProvider;
166+
}
167+
138168
return model;
139169
}
140170
}

src/EFCore/Properties/CoreStrings.Designer.cs

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)