Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 39 additions & 19 deletions src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,16 @@ protected override void ValidatePropertyMapping(

if (complexProperty.ComplexType.IsMappedToJson())
{
if (!complexProperty.DeclaringType.IsMappedToJson()
&& complexProperty.DeclaringType is IComplexType)
{
// Issue #36558
throw new InvalidOperationException(
RelationalStrings.NestedComplexPropertyJsonWithTableSharing(
$"{complexProperty.DeclaringType.DisplayName()}.{complexProperty.Name}",
complexProperty.DeclaringType.DisplayName()));
}

ValidateJsonProperties(complexProperty.ComplexType);
}
}
Expand Down Expand Up @@ -2702,7 +2712,7 @@ protected virtual void ValidateJsonEntities(
var nonJsonType = mappedTypes.Where(x => !x.IsMappedToJson()).First();

// must be an owned collection (mapped to a separate table) that owns a JSON type
// issue #28441
// Issue #28441
throw new InvalidOperationException(
RelationalStrings.JsonEntityOwnedByNonJsonOwnedType(
nonJsonType.DisplayName(), table.DisplayName()));
Expand All @@ -2711,7 +2721,7 @@ protected virtual void ValidateJsonEntities(
var distinctRootTypes = nonOwnedTypes.Select(x => x.GetRootType()).Distinct().ToList();
if (distinctRootTypes.Count > 1)
{
// issue #28442
// Issue #28442
throw new InvalidOperationException(
RelationalStrings.JsonEntityWithTableSplittingIsNotSupported);
}
Expand Down Expand Up @@ -2966,26 +2976,36 @@ private static Dictionary<string, string> ValidateJsonProperties(IConventionType
foreach (var property in typeBase.GetProperties())
{
var jsonPropertyName = property.GetJsonPropertyName();
if (!string.IsNullOrEmpty(jsonPropertyName))
if (string.IsNullOrEmpty(jsonPropertyName))
{
var columnNameAnnotation = property.FindAnnotation(RelationalAnnotationNames.ColumnName);
if (columnNameAnnotation != null && !string.IsNullOrEmpty((string?)columnNameAnnotation.Value))
{
throw new InvalidOperationException(
RelationalStrings.PropertyBothColumnNameAndJsonPropertyName(
$"{typeBase.DisplayName()}.{property.Name}",
(string)columnNameAnnotation.Value,
jsonPropertyName));
}
continue;
}

if (property.TryGetDefaultValue(out var _))
{
throw new InvalidOperationException(
RelationalStrings.JsonEntityWithDefaultValueSetOnItsProperty(
typeBase.DisplayName(), property.Name));
}
var columnNameAnnotation = property.FindAnnotation(RelationalAnnotationNames.ColumnName);
if (columnNameAnnotation != null && !string.IsNullOrEmpty((string?)columnNameAnnotation.Value))
{
throw new InvalidOperationException(
RelationalStrings.PropertyBothColumnNameAndJsonPropertyName(
$"{typeBase.DisplayName()}.{property.Name}",
(string)columnNameAnnotation.Value,
jsonPropertyName));
}

CheckUniqueness(jsonPropertyName, property.Name, typeBase, jsonPropertyNames);
if (property.TryGetDefaultValue(out var _))
{
// Issue #35934
throw new InvalidOperationException(
RelationalStrings.JsonEntityWithDefaultValueSetOnItsProperty(
typeBase.DisplayName(), property.Name));
}

CheckUniqueness(jsonPropertyName, property.Name, typeBase, jsonPropertyNames);

if (property.IsConcurrencyToken)
{
throw new InvalidOperationException(
RelationalStrings.ConcurrencyTokenOnJsonMappedProperty(
property.Name, typeBase.DisplayName()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,12 @@ private static void CreateContainerColumn<TColumnMappingBase>(

Check.DebugAssert(tableBase.FindColumn(containerColumnName) == null, $"Table '{tableBase.Name}' already has a '{containerColumnName}' column.");

var jsonColumnTypeMapping = relationalTypeMappingSource.FindMapping(typeof(JsonTypePlaceholder), storeTypeName: containerColumnType)!;
var jsonColumnTypeMapping = relationalTypeMappingSource.FindMapping(typeof(JsonTypePlaceholder), storeTypeName: containerColumnType);
if (jsonColumnTypeMapping == null)
{
throw new InvalidOperationException(RelationalStrings.UnsupportedJsonColumnType(containerColumnType ?? "null", containerColumnName, tableBase.Name));
}

var jsonColumn = createColumn(containerColumnName, containerColumnType, tableBase, jsonColumnTypeMapping);
tableBase.Columns.Add(containerColumnName, jsonColumn);

Expand Down
24 changes: 24 additions & 0 deletions src/EFCore.Relational/Properties/RelationalStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions src/EFCore.Relational/Properties/RelationalStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@
<data name="ComputedColumnSqlUnspecified" xml:space="preserve">
<value>The computed column SQL has not been specified for the column '{table}.{column}'. Specify the SQL before using Entity Framework to create the database schema.</value>
</data>
<data name="ConcurrencyTokenOnJsonMappedProperty" xml:space="preserve">
<value>The property '{property}' on '{type}' is configured as a concurrency token, but the type is mapped to JSON. Concurrency tokens are not supported on JSON-mapped types.</value>
</data>
<data name="ConflictingAmbientTransaction" xml:space="preserve">
<value>An ambient transaction has been detected. The ambient transaction needs to be completed before starting a new transaction on this connection.</value>
</data>
Expand Down Expand Up @@ -1003,6 +1006,9 @@
<data name="NestedCollectionsNotSupported" xml:space="preserve">
<value>The property '{propertyType} {type}.{property}' is a primitive collection of a primitive collection. Nested primitive collections are not yet supported with relational database providers.</value>
</data>
<data name="NestedComplexPropertyJsonWithTableSharing" xml:space="preserve">
<value>Complex property '{complexProperty}' is mapped to JSON but its containing type '{containingType}' is not. Map the root complex type to JSON. See https://github.com/dotnet/efcore/issues/36558</value>
</data>
<data name="NoActiveTransaction" xml:space="preserve">
<value>The connection does not have any active transactions.</value>
</data>
Expand Down Expand Up @@ -1285,6 +1291,9 @@
<data name="UnsupportedDataOperationStoreType" xml:space="preserve">
<value>The store type '{type}' used for the column '{column}' in a migration data operation is not supported by the current provider.</value>
</data>
<data name="UnsupportedJsonColumnType" xml:space="preserve">
<value>The store type '{storeType}' specified for JSON column '{columnName}' in table '{tableName}' is not supported by the current provider. JSON columns require a provider-specific JSON store type.</value>
</data>
<data name="UnsupportedOperatorForSqlExpression" xml:space="preserve">
<value>Unsupported operator '{nodeType}' specified for expression of type '{expressionType}'.</value>
</data>
Expand Down
4 changes: 2 additions & 2 deletions src/EFCore/ChangeTracking/Internal/ChangeDetector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ private void LogChangeDetected(IInternalEntry entry, IProperty property, object?
}
else
{
_logger.ComplexTypePropertyChangeDetectedSensitive((InternalComplexEntry)entry, property, original, current);
_logger.ComplexElementPropertyChangeDetectedSensitive((InternalComplexEntry)entry, property, original, current);
}
}
else
Expand All @@ -761,7 +761,7 @@ private void LogChangeDetected(IInternalEntry entry, IProperty property, object?
}
else
{
_logger.ComplexTypePropertyChangeDetected((InternalComplexEntry)entry, property, original, current);
_logger.ComplexElementPropertyChangeDetected((InternalComplexEntry)entry, property, original, current);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/EFCore/Diagnostics/CoreEventId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ private enum Id
StateChanged,
ValueGenerated,
SkipCollectionChangeDetected,
ComplexTypePropertyChangeDetected
ComplexElementPropertyChangeDetected
}

private static readonly string _updatePrefix = DbLoggerCategory.Update.Name + ".";
Expand Down Expand Up @@ -986,7 +986,7 @@ private static EventId MakeChangeTrackingId(Id id)
/// <see cref="DiagnosticSource" />.
/// </para>
/// </remarks>
public static readonly EventId ComplexTypePropertyChangeDetected = MakeChangeTrackingId(Id.ComplexTypePropertyChangeDetected);
public static readonly EventId ComplexElementPropertyChangeDetected = MakeChangeTrackingId(Id.ComplexElementPropertyChangeDetected);

/// <summary>
/// DetectChanges has detected a change in a foreign key property value.
Expand Down
20 changes: 10 additions & 10 deletions src/EFCore/Diagnostics/CoreLoggerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2290,21 +2290,21 @@ private static string PropertyChangeDetectedSensitive(EventDefinitionBase defini
}

/// <summary>
/// Logs for the <see cref="CoreEventId.ComplexTypePropertyChangeDetected" /> event.
/// Logs for the <see cref="CoreEventId.ComplexElementPropertyChangeDetected" /> event.
/// </summary>
/// <param name="diagnostics">The diagnostics logger to use.</param>
/// <param name="internalComplexEntry">The internal complex entry.</param>
/// <param name="property">The property.</param>
/// <param name="oldValue">The old value.</param>
/// <param name="newValue">The new value.</param>
public static void ComplexTypePropertyChangeDetected(
public static void ComplexElementPropertyChangeDetected(
this IDiagnosticsLogger<DbLoggerCategory.ChangeTracking> diagnostics,
InternalComplexEntry internalComplexEntry,
IProperty property,
object? oldValue,
object? newValue)
{
var definition = CoreResources.LogComplexTypePropertyChangeDetected(diagnostics);
var definition = CoreResources.LogComplexElementPropertyChangeDetected(diagnostics);

if (diagnostics.ShouldLog(definition))
{
Expand All @@ -2315,7 +2315,7 @@ public static void ComplexTypePropertyChangeDetected(
{
var eventData = new ComplexTypePropertyChangedEventData(
definition,
ComplexTypePropertyChangeDetected,
ComplexElementPropertyChangeDetected,
new ComplexElementEntry(internalComplexEntry),
property,
oldValue,
Expand All @@ -2325,7 +2325,7 @@ public static void ComplexTypePropertyChangeDetected(
}
}

private static string ComplexTypePropertyChangeDetected(EventDefinitionBase definition, EventData payload)
private static string ComplexElementPropertyChangeDetected(EventDefinitionBase definition, EventData payload)
{
var d = (EventDefinition<string, string>)definition;
var p = (ComplexTypePropertyChangedEventData)payload;
Expand All @@ -2335,21 +2335,21 @@ private static string ComplexTypePropertyChangeDetected(EventDefinitionBase defi
}

/// <summary>
/// Logs for the <see cref="CoreEventId.ComplexTypePropertyChangeDetected" /> event.
/// Logs for the <see cref="CoreEventId.ComplexElementPropertyChangeDetected" /> event.
/// </summary>
/// <param name="diagnostics">The diagnostics logger to use.</param>
/// <param name="internalComplexEntry">The internal complex entry.</param>
/// <param name="property">The property.</param>
/// <param name="oldValue">The old value.</param>
/// <param name="newValue">The new value.</param>
public static void ComplexTypePropertyChangeDetectedSensitive(
public static void ComplexElementPropertyChangeDetectedSensitive(
this IDiagnosticsLogger<DbLoggerCategory.ChangeTracking> diagnostics,
InternalComplexEntry internalComplexEntry,
IProperty property,
object? oldValue,
object? newValue)
{
var definition = CoreResources.LogComplexTypePropertyChangeDetectedSensitive(diagnostics);
var definition = CoreResources.LogComplexElementPropertyChangeDetectedSensitive(diagnostics);

if (diagnostics.ShouldLog(definition))
{
Expand All @@ -2366,7 +2366,7 @@ public static void ComplexTypePropertyChangeDetectedSensitive(
{
var eventData = new ComplexTypePropertyChangedEventData(
definition,
ComplexTypePropertyChangeDetectedSensitive,
ComplexElementPropertyChangeDetectedSensitive,
new ComplexElementEntry(internalComplexEntry),
property,
oldValue,
Expand All @@ -2376,7 +2376,7 @@ public static void ComplexTypePropertyChangeDetectedSensitive(
}
}

private static string ComplexTypePropertyChangeDetectedSensitive(EventDefinitionBase definition, EventData payload)
private static string ComplexElementPropertyChangeDetectedSensitive(EventDefinitionBase definition, EventData payload)
{
var d = (EventDefinition<string, string, object?, object?, string>)definition;
var p = (ComplexTypePropertyChangedEventData)payload;
Expand Down
4 changes: 2 additions & 2 deletions src/EFCore/Diagnostics/LoggingDefinitions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ public abstract class LoggingDefinitions
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
[EntityFrameworkInternal]
public EventDefinitionBase? LogComplexTypePropertyChangeDetected;
public EventDefinitionBase? LogComplexElementPropertyChangeDetected;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -536,7 +536,7 @@ public abstract class LoggingDefinitions
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
[EntityFrameworkInternal]
public EventDefinitionBase? LogComplexTypePropertyChangeDetectedSensitive;
public EventDefinitionBase? LogComplexElementPropertyChangeDetectedSensitive;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down
8 changes: 8 additions & 0 deletions src/EFCore/Infrastructure/ModelValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,14 @@ protected virtual void ValidatePropertyMapping(IConventionComplexProperty comple
CoreStrings.ComplexValueTypeShadowProperty(complexProperty.ComplexType.DisplayName(), shadowProperty.Name));
}
}

// Issue #35613: Shadow properties on all complex types are not supported
var shadowPropertyOnComplexType = complexProperty.ComplexType.GetDeclaredProperties().FirstOrDefault(p => p.IsShadowProperty());
if (shadowPropertyOnComplexType != null)
{
throw new InvalidOperationException(
CoreStrings.ComplexTypeShadowProperty(complexProperty.ComplexType.DisplayName(), shadowPropertyOnComplexType.Name));
}
}

/// <summary>
Expand Down
9 changes: 9 additions & 0 deletions src/EFCore/Metadata/IConventionNavigationBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ namespace Microsoft.EntityFrameworkCore.Metadata;
/// </remarks>
public interface IConventionNavigationBase : IReadOnlyNavigationBase, IConventionPropertyBase
{
/// <summary>
/// Gets the entity type that this navigation property will hold an instance(s) of.
/// </summary>
new IConventionEntityType TargetEntityType
{
[DebuggerStepThrough]
get => (IConventionEntityType)((IReadOnlyNavigationBase)this).TargetEntityType;
}

/// <summary>
/// Sets a value indicating whether this navigation should be eager loaded by default.
/// </summary>
Expand Down
Loading
Loading