Skip to content

Commit ce56d77

Browse files
authored
Allow configuring the column type with ToJson() (#36478)
Map the discriminator property to $type in JSON by default
1 parent 794e359 commit ce56d77

11 files changed

+376
-148
lines changed

src/EFCore.Relational/Extensions/RelationalComplexCollectionBuilderExtensions.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,31 @@ public static ComplexCollectionBuilder<TComplex> HasJsonPropertyName<TComplex>(
7171
string? name)
7272
where TComplex : notnull
7373
=> (ComplexCollectionBuilder<TComplex>)HasJsonPropertyName((ComplexCollectionBuilder)complexCollectionBuilder, name);
74+
75+
/// <summary>
76+
/// Configures the column type for the JSON column that stores the complex collection.
77+
/// </summary>
78+
/// <param name="complexCollectionBuilder">The builder for the complex collection being configured.</param>
79+
/// <param name="columnType">The database type for the column, or <see langword="null" /> to use the database default.</param>
80+
/// <returns>The same builder instance so that multiple calls can be chained.</returns>
81+
public static ComplexCollectionBuilder HasColumnType(
82+
this ComplexCollectionBuilder complexCollectionBuilder,
83+
string? columnType)
84+
{
85+
complexCollectionBuilder.Metadata.ComplexType.SetContainerColumnType(columnType);
86+
87+
return complexCollectionBuilder;
88+
}
89+
90+
/// <summary>
91+
/// Configures the column type for the JSON column that stores the complex collection.
92+
/// </summary>
93+
/// <param name="complexCollectionBuilder">The builder for the complex collection being configured.</param>
94+
/// <param name="columnType">The database type for the column, or <see langword="null" /> to use the database default.</param>
95+
/// <returns>The same builder instance so that multiple calls can be chained.</returns>
96+
public static ComplexCollectionBuilder<TComplex> HasColumnType<TComplex>(
97+
this ComplexCollectionBuilder<TComplex> complexCollectionBuilder,
98+
string? columnType)
99+
where TComplex : notnull
100+
=> (ComplexCollectionBuilder<TComplex>)HasColumnType((ComplexCollectionBuilder)complexCollectionBuilder, columnType);
74101
}

src/EFCore.Relational/Extensions/RelationalComplexPropertyBuilderExtensions.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,31 @@ public static ComplexPropertyBuilder<TComplex> HasJsonPropertyName<TComplex>(
7272
string? name)
7373
where TComplex : notnull
7474
=> (ComplexPropertyBuilder<TComplex>)HasJsonPropertyName((ComplexPropertyBuilder)complexPropertyBuilder, name);
75+
76+
/// <summary>
77+
/// Configures the column type for the JSON column that stores the complex property.
78+
/// </summary>
79+
/// <param name="complexPropertyBuilder">The builder for the complex property being configured.</param>
80+
/// <param name="columnType">The database type for the column, or <see langword="null" /> to use the database default.</param>
81+
/// <returns>The same builder instance so that multiple calls can be chained.</returns>
82+
public static ComplexPropertyBuilder HasColumnType(
83+
this ComplexPropertyBuilder complexPropertyBuilder,
84+
string? columnType)
85+
{
86+
complexPropertyBuilder.Metadata.ComplexType.SetContainerColumnType(columnType);
87+
88+
return complexPropertyBuilder;
89+
}
90+
91+
/// <summary>
92+
/// Configures the column type for the JSON column that stores the complex property.
93+
/// </summary>
94+
/// <param name="complexPropertyBuilder">The builder for the complex property being configured.</param>
95+
/// <param name="columnType">The database type for the column, or <see langword="null" /> to use the database default.</param>
96+
/// <returns>The same builder instance so that multiple calls can be chained.</returns>
97+
public static ComplexPropertyBuilder<TComplex> HasColumnType<TComplex>(
98+
this ComplexPropertyBuilder<TComplex> complexPropertyBuilder,
99+
string? columnType)
100+
where TComplex : notnull
101+
=> (ComplexPropertyBuilder<TComplex>)HasColumnType((ComplexPropertyBuilder)complexPropertyBuilder, columnType);
75102
}

src/EFCore.Relational/Extensions/RelationalComplexTypeExtensions.cs

Lines changed: 0 additions & 55 deletions
This file was deleted.

src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs

Lines changed: 0 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,87 +1564,6 @@ public static void SetMappingStrategy(this IMutableEntityType entityType, string
15641564
public static bool IsMappedToJson(this IReadOnlyEntityType entityType)
15651565
=> !string.IsNullOrEmpty(entityType.GetContainerColumnName());
15661566

1567-
/// <summary>
1568-
/// Sets the name of the container column to which the entity type is mapped.
1569-
/// </summary>
1570-
/// <param name="entityType">The entity type to set the container column name for.</param>
1571-
/// <param name="columnName">The name to set.</param>
1572-
public static void SetContainerColumnName(this IMutableEntityType entityType, string? columnName)
1573-
=> entityType.SetOrRemoveAnnotation(RelationalAnnotationNames.ContainerColumnName, columnName);
1574-
1575-
/// <summary>
1576-
/// Sets the name of the container column to which the entity type is mapped.
1577-
/// </summary>
1578-
/// <param name="entityType">The entity type to set the container column name for.</param>
1579-
/// <param name="columnName">The name to set.</param>
1580-
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
1581-
/// <returns>The configured value.</returns>
1582-
public static string? SetContainerColumnName(
1583-
this IConventionEntityType entityType,
1584-
string? columnName,
1585-
bool fromDataAnnotation = false)
1586-
=> (string?)entityType.SetAnnotation(RelationalAnnotationNames.ContainerColumnName, columnName, fromDataAnnotation)?.Value;
1587-
1588-
/// <summary>
1589-
/// Gets the <see cref="ConfigurationSource" /> for the container column name.
1590-
/// </summary>
1591-
/// <param name="entityType">The entity type to set the container column name for.</param>
1592-
/// <returns>The <see cref="ConfigurationSource" /> for the container column name.</returns>
1593-
public static ConfigurationSource? GetContainerColumnNameConfigurationSource(this IConventionEntityType entityType)
1594-
=> entityType.FindAnnotation(RelationalAnnotationNames.ContainerColumnName)
1595-
?.GetConfigurationSource();
1596-
1597-
/// <summary>
1598-
/// Gets the container column name to which the entity type is mapped.
1599-
/// </summary>
1600-
/// <param name="entityType">The entity type to get the container column name for.</param>
1601-
/// <returns>The container column name to which the entity type is mapped.</returns>
1602-
public static string? GetContainerColumnName(this IReadOnlyEntityType entityType)
1603-
{
1604-
var containerColumnName = entityType.FindAnnotation(RelationalAnnotationNames.ContainerColumnName);
1605-
return containerColumnName == null
1606-
? entityType.FindOwnership()?.PrincipalEntityType.GetContainerColumnName()
1607-
: (string?)containerColumnName.Value;
1608-
}
1609-
1610-
/// <summary>
1611-
/// Sets the column type to use for the container column to which the entity type is mapped.
1612-
/// </summary>
1613-
/// <param name="entityType">The entity type.</param>
1614-
/// <param name="columnType">The database column type.</param>
1615-
public static void SetContainerColumnType(this IMutableEntityType entityType, string? columnType)
1616-
=> entityType.SetOrRemoveAnnotation(RelationalAnnotationNames.ContainerColumnType, columnType);
1617-
1618-
/// <summary>
1619-
/// Sets the column type to use for the container column to which the entity type is mapped.
1620-
/// </summary>
1621-
/// <param name="entityType">The entity type.</param>
1622-
/// <param name="columnType">The database column type.</param>
1623-
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
1624-
/// <returns>The configured value.</returns>
1625-
public static string? SetContainerColumnType(
1626-
this IConventionEntityType entityType,
1627-
string? columnType,
1628-
bool fromDataAnnotation = false)
1629-
=> (string?)entityType.SetAnnotation(RelationalAnnotationNames.ContainerColumnType, columnType, fromDataAnnotation)?.Value;
1630-
1631-
/// <summary>
1632-
/// Gets the <see cref="ConfigurationSource" /> for the container column type.
1633-
/// </summary>
1634-
/// <param name="entityType">The entity type.</param>
1635-
/// <returns>The <see cref="ConfigurationSource" />.</returns>
1636-
public static ConfigurationSource? GetContainerColumnTypeConfigurationSource(this IConventionEntityType entityType)
1637-
=> entityType.FindAnnotation(RelationalAnnotationNames.ContainerColumnType)
1638-
?.GetConfigurationSource();
1639-
1640-
/// <summary>
1641-
/// Gets the column type to use for the container column to which the entity type is mapped.
1642-
/// </summary>
1643-
/// <param name="entityType">The entity type.</param>
1644-
/// <returns>The database column type.</returns>
1645-
public static string? GetContainerColumnType(this IReadOnlyEntityType entityType)
1646-
=> entityType.FindAnnotation(RelationalAnnotationNames.ContainerColumnType)?.Value as string;
1647-
16481567
/// <summary>
16491568
/// Sets the type mapping for the container column to which the entity type is mapped.
16501569
/// </summary>

src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2047,11 +2047,14 @@ private static TValue ThrowReadValueException<TValue>(
20472047
/// <param name="property">The property.</param>
20482048
/// <returns>
20492049
/// The value for the JSON property used to store the value of this entity property.
2050-
/// <see langword="null" /> is returned for key properties and for properties of entities that are not mapped to a JSON column.
2050+
/// By default <see langword="null" /> is returned for key properties and for properties that
2051+
/// are not mapped to JSON.
20512052
/// </returns>
20522053
public static string? GetJsonPropertyName(this IReadOnlyProperty property)
20532054
=> (string?)property.FindAnnotation(RelationalAnnotationNames.JsonPropertyName)?.Value
2054-
?? (property.IsKey() || !property.DeclaringType.IsMappedToJson() ? null : property.Name);
2055+
?? (property.IsKey() || !property.DeclaringType.IsMappedToJson()
2056+
? null
2057+
: property == property.DeclaringType.FindDiscriminatorProperty() ? "$type" : property.Name);
20552058

20562059
/// <summary>
20572060
/// Sets the value of JSON property name used for the given property of an entity mapped to a JSON column.

src/EFCore.Relational/Extensions/RelationalTypeBaseExtensions.cs

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// ReSharper disable once CheckNamespace
55

66
using Microsoft.EntityFrameworkCore.Internal;
7+
using Microsoft.EntityFrameworkCore.Metadata.Internal;
78

89
namespace Microsoft.EntityFrameworkCore;
910

@@ -370,20 +371,87 @@ public static bool IsMappedToJson(this IReadOnlyTypeBase typeBase)
370371
/// <param name="typeBase">The type to get the container column name for.</param>
371372
/// <returns>The container column name to which the type is mapped.</returns>
372373
public static string? GetContainerColumnName(this IReadOnlyTypeBase typeBase)
373-
=> typeBase is IReadOnlyEntityType entityType
374-
? entityType.GetContainerColumnName()
375-
: ((IReadOnlyComplexType)typeBase).GetContainerColumnName();
374+
{
375+
var containerColumnName = typeBase.FindAnnotation(RelationalAnnotationNames.ContainerColumnName);
376+
return containerColumnName != null
377+
? (string?)containerColumnName.Value
378+
: typeBase is IReadOnlyEntityType entityType
379+
? entityType.FindOwnership()?.PrincipalEntityType.GetContainerColumnName()
380+
: ((IReadOnlyComplexType)typeBase).ComplexProperty.DeclaringType.GetContainerColumnName();
381+
}
382+
383+
/// <summary>
384+
/// Sets the name of the container column to which the type is mapped.
385+
/// </summary>
386+
/// <param name="typeBase">The type to set the container column name for.</param>
387+
/// <param name="columnName">The name to set.</param>
388+
public static void SetContainerColumnName(this IMutableTypeBase typeBase, string? columnName)
389+
=> typeBase.SetOrRemoveAnnotation(RelationalAnnotationNames.ContainerColumnName, columnName);
390+
391+
/// <summary>
392+
/// Sets the name of the container column to which the type is mapped.
393+
/// </summary>
394+
/// <param name="typeBase">The type to set the container column name for.</param>
395+
/// <param name="columnName">The name to set.</param>
396+
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
397+
/// <returns>The configured value.</returns>
398+
public static string? SetContainerColumnName(
399+
this IConventionTypeBase typeBase,
400+
string? columnName,
401+
bool fromDataAnnotation = false)
402+
=> (string?)typeBase.SetAnnotation(RelationalAnnotationNames.ContainerColumnName, columnName, fromDataAnnotation)?.Value;
403+
404+
/// <summary>
405+
/// Gets the <see cref="ConfigurationSource" /> for the container column name.
406+
/// </summary>
407+
/// <param name="typeBase">The type to get the container column name configuration source for.</param>
408+
/// <returns>The <see cref="ConfigurationSource" /> for the container column name.</returns>
409+
public static ConfigurationSource? GetContainerColumnNameConfigurationSource(this IConventionTypeBase typeBase)
410+
=> typeBase.FindAnnotation(RelationalAnnotationNames.ContainerColumnName)
411+
?.GetConfigurationSource();
376412

377413
/// <summary>
378414
/// Gets the column type to use for the container column to which the type is mapped.
379415
/// </summary>
380416
/// <param name="typeBase">The type.</param>
381417
/// <returns>The database column type.</returns>
382418
public static string? GetContainerColumnType(this IReadOnlyTypeBase typeBase)
383-
=> typeBase is IReadOnlyEntityType entityType
384-
? entityType.GetContainerColumnType()
385-
: null;
419+
=> typeBase.FindAnnotation(RelationalAnnotationNames.ContainerColumnType)?.Value is string columnName
420+
? columnName
421+
: typeBase is IReadOnlyEntityType entityType
422+
? entityType.FindOwnership()?.PrincipalEntityType.GetContainerColumnType()
423+
: ((IReadOnlyComplexType)typeBase).ComplexProperty.DeclaringType.GetContainerColumnType();
424+
425+
/// <summary>
426+
/// Sets the type of the container column to which the type is mapped.
427+
/// </summary>
428+
/// <param name="typeBase">The type to set the container column type for.</param>
429+
/// <param name="columnType">The type to set.</param>
430+
public static void SetContainerColumnType(this IMutableTypeBase typeBase, string? columnType)
431+
=> typeBase.SetOrRemoveAnnotation(RelationalAnnotationNames.ContainerColumnType, columnType);
386432

433+
/// <summary>
434+
/// Sets the type of the container column to which the type is mapped.
435+
/// </summary>
436+
/// <param name="typeBase">The type to set the container column type for.</param>
437+
/// <param name="columnType">The type to set.</param>
438+
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
439+
/// <returns>The configured value.</returns>
440+
public static string? SetContainerColumnType(
441+
this IConventionTypeBase typeBase,
442+
string? columnType,
443+
bool fromDataAnnotation = false)
444+
=> (string?)typeBase.SetAnnotation(RelationalAnnotationNames.ContainerColumnType, columnType, fromDataAnnotation)?.Value;
445+
446+
/// <summary>
447+
/// Gets the <see cref="ConfigurationSource" /> for the container column type.
448+
/// </summary>
449+
/// <param name="typeBase">The type to get the container column type configuration source for.</param>
450+
/// <returns>The <see cref="ConfigurationSource" /> for the container column type.</returns>
451+
public static ConfigurationSource? GetContainerColumnTypeConfigurationSource(this IConventionTypeBase typeBase)
452+
=> typeBase.FindAnnotation(RelationalAnnotationNames.ContainerColumnType)
453+
?.GetConfigurationSource();
454+
387455
/// <summary>
388456
/// Gets the value of JSON property name used for the given entity mapped to a JSON column.
389457
/// </summary>

0 commit comments

Comments
 (0)