diff --git a/src/EFCore.Cosmos/Metadata/Conventions/CosmosDiscriminatorConvention.cs b/src/EFCore.Cosmos/Metadata/Conventions/CosmosDiscriminatorConvention.cs
index 2990718e5c0..1e007b7aaf5 100644
--- a/src/EFCore.Cosmos/Metadata/Conventions/CosmosDiscriminatorConvention.cs
+++ b/src/EFCore.Cosmos/Metadata/Conventions/CosmosDiscriminatorConvention.cs
@@ -83,11 +83,10 @@ private static void ProcessEntityType(IConventionEntityTypeBuilder entityTypeBui
{
return;
}
-
if (entityType.IsDocumentRoot())
{
- entityTypeBuilder.HasDiscriminator(entityType.Model.GetEmbeddedDiscriminatorName(), typeof(string))
- ?.HasValue(entityType, entityType.ShortName());
+ var discriminator = HasDiscriminator(entityTypeBuilder);
+ discriminator?.HasValue(entityType, entityType.ShortName());
}
else
{
@@ -125,7 +124,7 @@ public override void ProcessEntityTypeBaseTypeChanged(
{
if (entityType.IsDocumentRoot())
{
- entityTypeBuilder.HasDiscriminator(entityType.Model.GetEmbeddedDiscriminatorName(), typeof(string));
+ HasDiscriminator(entityTypeBuilder);
}
}
else
@@ -137,7 +136,7 @@ public override void ProcessEntityTypeBaseTypeChanged(
return;
}
- var discriminator = rootType.Builder.HasDiscriminator(entityType.Model.GetEmbeddedDiscriminatorName(), typeof(string));
+ var discriminator = HasDiscriminator(rootType.Builder);
if (discriminator != null)
{
SetDefaultDiscriminatorValues(entityTypeBuilder.Metadata.GetDerivedTypesInclusive(), discriminator);
@@ -145,6 +144,20 @@ public override void ProcessEntityTypeBaseTypeChanged(
}
}
+ private static IConventionDiscriminatorBuilder? HasDiscriminator(IConventionEntityTypeBuilder entityTypeBuilder)
+ {
+ var discriminator = entityTypeBuilder.HasDiscriminator(typeof(string));
+ var discriminatorProperty = discriminator?.EntityType.FindDiscriminatorProperty();
+ if (discriminatorProperty != null)
+ {
+ CosmosPropertyBuilderExtensions.ToJsonProperty(
+ discriminatorProperty.Builder,
+ entityTypeBuilder.Metadata.Model.GetEmbeddedDiscriminatorName());
+ }
+
+ return discriminator;
+ }
+
///
protected override void SetDefaultDiscriminatorValues(
IEnumerable entityTypes,
diff --git a/src/EFCore.Design/Design/Internal/CSharpHelper.cs b/src/EFCore.Design/Design/Internal/CSharpHelper.cs
index c62d8194854..953469b0133 100644
--- a/src/EFCore.Design/Design/Internal/CSharpHelper.cs
+++ b/src/EFCore.Design/Design/Internal/CSharpHelper.cs
@@ -1633,72 +1633,8 @@ public virtual string Expression(
}
private static bool IsIdentifierStartCharacter(char ch)
- {
- if (ch < 'a')
- {
- return ch is >= 'A' and (<= 'Z' or '_');
- }
-
- if (ch <= 'z')
- {
- return true;
- }
-
- return ch > '\u007F' && IsLetterChar(CharUnicodeInfo.GetUnicodeCategory(ch));
- }
+ => char.IsLetter(ch) || ch == '_';
private static bool IsIdentifierPartCharacter(char ch)
- {
- if (ch < 'a')
- {
- return (ch < 'A'
- ? ch is >= '0' and <= '9'
- : ch <= 'Z')
- || ch == '_';
- }
-
- if (ch <= 'z')
- {
- return true;
- }
-
- if (ch <= '\u007F')
- {
- return false;
- }
-
- var cat = CharUnicodeInfo.GetUnicodeCategory(ch);
- if (IsLetterChar(cat))
- {
- return true;
- }
-
- switch (cat)
- {
- case UnicodeCategory.DecimalDigitNumber:
- case UnicodeCategory.ConnectorPunctuation:
- case UnicodeCategory.NonSpacingMark:
- case UnicodeCategory.SpacingCombiningMark:
- case UnicodeCategory.Format:
- return true;
- }
-
- return false;
- }
-
- private static bool IsLetterChar(UnicodeCategory cat)
- {
- switch (cat)
- {
- case UnicodeCategory.UppercaseLetter:
- case UnicodeCategory.LowercaseLetter:
- case UnicodeCategory.TitlecaseLetter:
- case UnicodeCategory.ModifierLetter:
- case UnicodeCategory.OtherLetter:
- case UnicodeCategory.LetterNumber:
- return true;
- }
-
- return false;
- }
+ => char.IsLetter(ch) || char.IsAsciiDigit(ch) || ch == '_';
}
diff --git a/src/EFCore/Diagnostics/CoreEventId.cs b/src/EFCore/Diagnostics/CoreEventId.cs
index e68acbf39c9..8ec0fc0f7fb 100644
--- a/src/EFCore/Diagnostics/CoreEventId.cs
+++ b/src/EFCore/Diagnostics/CoreEventId.cs
@@ -129,6 +129,7 @@ private enum Id
NoEntityTypeConfigurationsWarning = CoreBaseId + 632,
AccidentalEntityType = CoreBaseId + 633,
AccidentalComplexPropertyCollection = CoreBaseId + 634,
+ ShadowPropertyNameNotValidIdentifierWarning = CoreBaseId + 635,
// ChangeTracking events
DetectChangesStarting = CoreBaseId + 800,
@@ -750,6 +751,20 @@ private static EventId MakeModelValidationId(Id id)
///
public static readonly EventId AccidentalComplexPropertyCollection = MakeModelValidationId(Id.AccidentalComplexPropertyCollection);
+ ///
+ /// A shadow property has a name that is not a valid identifier, which can cause issues in generated code.
+ ///
+ ///
+ ///
+ /// This event is in the category.
+ ///
+ ///
+ /// This event uses the payload when used with a .
+ ///
+ ///
+ public static readonly EventId ShadowPropertyNameNotValidIdentifierWarning =
+ MakeModelValidationId(Id.ShadowPropertyNameNotValidIdentifierWarning);
+
///
/// The on the collection navigation property was ignored.
///
diff --git a/src/EFCore/Diagnostics/CoreLoggerExtensions.cs b/src/EFCore/Diagnostics/CoreLoggerExtensions.cs
index 3b76077e276..79e201f5fe1 100644
--- a/src/EFCore/Diagnostics/CoreLoggerExtensions.cs
+++ b/src/EFCore/Diagnostics/CoreLoggerExtensions.cs
@@ -1432,6 +1432,40 @@ private static string ShadowPropertyCreated(EventDefinitionBase definition, Even
return d.GenerateMessage(p.Property.DeclaringType.DisplayName(), p.Property.Name);
}
+ ///
+ /// Logs for the event.
+ ///
+ /// The diagnostics logger to use.
+ /// The property.
+ public static void ShadowPropertyNameNotValidIdentifierWarning(
+ this IDiagnosticsLogger diagnostics,
+ IProperty property)
+ {
+ var definition = CoreResources.LogShadowPropertyNameNotValidIdentifier(diagnostics);
+
+ if (diagnostics.ShouldLog(definition))
+ {
+ definition.Log(diagnostics, property.DeclaringType.DisplayName(), property.Name);
+ }
+
+ if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = new PropertyEventData(
+ definition,
+ ShadowPropertyNameNotValidIdentifier,
+ property);
+
+ diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
+ }
+ }
+
+ private static string ShadowPropertyNameNotValidIdentifier(EventDefinitionBase definition, EventData payload)
+ {
+ var d = (EventDefinition)definition;
+ var p = (PropertyEventData)payload;
+ return d.GenerateMessage(p.Property.DeclaringType.DisplayName(), p.Property.Name);
+ }
+
///
/// Logs for the event.
///
diff --git a/src/EFCore/Diagnostics/LoggingDefinitions.cs b/src/EFCore/Diagnostics/LoggingDefinitions.cs
index 55133e004e3..d0498c902df 100644
--- a/src/EFCore/Diagnostics/LoggingDefinitions.cs
+++ b/src/EFCore/Diagnostics/LoggingDefinitions.cs
@@ -825,4 +825,13 @@ public abstract class LoggingDefinitions
///
[EntityFrameworkInternal]
public EventDefinitionBase? LogQueryCompilationStarting;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [EntityFrameworkInternal]
+ public EventDefinitionBase? LogShadowPropertyNameNotValidIdentifier;
}
diff --git a/src/EFCore/EFCore.baseline.json b/src/EFCore/EFCore.baseline.json
index 7c9812b71e2..23cc7464300 100644
--- a/src/EFCore/EFCore.baseline.json
+++ b/src/EFCore/EFCore.baseline.json
@@ -3092,6 +3092,9 @@
{
"Member": "static readonly Microsoft.Extensions.Logging.EventId ShadowPropertyCreated"
},
+ {
+ "Member": "static readonly Microsoft.Extensions.Logging.EventId ShadowPropertyNameNotValidIdentifierWarning"
+ },
{
"Member": "static readonly Microsoft.Extensions.Logging.EventId SkipCollectionChangeDetected"
},
@@ -3355,6 +3358,9 @@
{
"Member": "static void ShadowPropertyCreated(this Microsoft.EntityFrameworkCore.Diagnostics.IDiagnosticsLogger diagnostics, Microsoft.EntityFrameworkCore.Metadata.IProperty property);"
},
+ {
+ "Member": "static void ShadowPropertyNameNotValidIdentifierWarning(this Microsoft.EntityFrameworkCore.Diagnostics.IDiagnosticsLogger diagnostics, Microsoft.EntityFrameworkCore.Metadata.IProperty property);"
+ },
{
"Member": "static void SkipCollectionChangeDetected(this Microsoft.EntityFrameworkCore.Diagnostics.IDiagnosticsLogger diagnostics, Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry internalEntityEntry, Microsoft.EntityFrameworkCore.Metadata.ISkipNavigation navigation, System.Collections.Generic.ISet