Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query: Use GetDiscriminatorValue for TPT and TPC #28070

Merged
1 commit merged into from
May 27, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -1391,6 +1391,30 @@ protected override void ValidateInheritanceMapping(

ValidateNonTphMapping(entityType, forTables: false);
ValidateNonTphMapping(entityType, forTables: true);

var derivedTypes = entityType.GetDerivedTypesInclusive().ToList();
var discriminatorValues = new Dictionary<string, IEntityType>();
foreach (var derivedType in derivedTypes)
{
if (!derivedType.ClrType.IsInstantiable())
{
continue;
}
var discriminatorValue = derivedType.GetDiscriminatorValue();
if (discriminatorValue is not string valueString)
{
throw new InvalidOperationException(
RelationalStrings.NonTphDiscriminatorValueNotString(discriminatorValue, derivedType.DisplayName()));
}

if (discriminatorValues.TryGetValue(valueString, out var duplicateEntityType))
{
throw new InvalidOperationException(RelationalStrings.EntityShortNameNotUnique(
derivedType.Name, discriminatorValue, duplicateEntityType.Name));
}

discriminatorValues[valueString] = derivedType;
}
}
}

Expand Down Expand Up @@ -1469,7 +1493,7 @@ private static void ValidateNonTphMapping(IEntityType rootEntityType, bool forTa
{
return;
}

var internalForeignKey = rootEntityType.FindRowInternalForeignKeys(storeObject.Value).FirstOrDefault();
if (internalForeignKey != null
&& derivedTypes.Count > 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,6 @@ public static IEnumerable<ITableMappingBase> GetViewOrTableMappings(this IEntity
?? entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.TableMappings))
?? Enumerable.Empty<ITableMappingBase>();

/// <summary>
/// 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.
/// </summary>
public static IReadOnlyList<string> GetTptDiscriminatorValues(this IReadOnlyEntityType entityType)
=> entityType.GetConcreteDerivedTypesInclusive().Select(et => et.ShortName()).ToList();

/// <summary>
/// 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
Expand Down
16 changes: 16 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.

6 changes: 6 additions & 0 deletions src/EFCore.Relational/Properties/RelationalStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,9 @@
<data name="EitherOfTwoValuesMustBeNull" xml:space="preserve">
<value>Either {param1} or {param2} must be null.</value>
</data>
<data name="EntityShortNameNotUnique" xml:space="preserve">
<value>The short name for '{entityType1}' is '{discriminatorValue}' which is the same for '{entityType2}'. Every concrete entity type in the hierarchy must have a unique short name. Either rename one of the types or call modelBuilder.Entity&lt;TEntity&gt;().Metadata.SetDiscriminatorValue("NewShortName").</value>
</data>
<data name="ErrorMaterializingProperty" xml:space="preserve">
<value>An error occurred while reading a database value for property '{entityType}.{property}'. See the inner exception for more information.</value>
</data>
Expand Down Expand Up @@ -763,6 +766,9 @@
<data name="NonScalarFunctionParameterCannotPropagatesNullability" xml:space="preserve">
<value>Cannot set 'PropagatesNullability' on parameter '{parameterName}' of DbFunction '{functionName}' since function does not represent a scalar function.</value>
</data>
<data name="NonTphDiscriminatorValueNotString" xml:space="preserve">
<value>The specified discriminator value '{value}' for '{entityType}' is not a string. Configure a string discriminator value instead.</value>
</data>
<data name="NonTphMappingStrategy" xml:space="preserve">
<value>The mapping strategy '{mappingStrategy}' specified on '{entityType}' is not supported for entity types with a discriminator.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public virtual EntityProjectionExpression UpdateEntityType(IEntityType derivedTy
var discriminatorExpression = DiscriminatorExpression;
if (DiscriminatorExpression is CaseExpression caseExpression)
{
var entityTypesToSelect = derivedType.GetTptDiscriminatorValues();
var entityTypesToSelect = derivedType.GetConcreteDerivedTypesInclusive().Select(e => e.GetDiscriminatorValue()).ToList();
var whenClauses = caseExpression.WhenClauses
.Where(wc => entityTypesToSelect.Contains((string)((SqlConstantExpression)wc.Result).Value!))
.ToList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1128,7 +1128,7 @@ protected override Expression VisitTypeBinary(TypeBinaryExpression typeBinaryExp
}

// TPT or TPC
var discriminatorValues = derivedType.GetTptDiscriminatorValues();
var discriminatorValues = derivedType.GetConcreteDerivedTypesInclusive().Select(e => e.GetDiscriminatorValue()).ToList();
if (entityReferenceExpression.SubqueryEntity != null)
{
var entityShaper = (EntityShaperExpression)entityReferenceExpression.SubqueryEntity.ShaperExpression;
Expand Down
15 changes: 11 additions & 4 deletions src/EFCore/Infrastructure/ConventionAnnotatable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,16 @@ public override void SetAnnotation(string name, object? value)
/// Removes the given annotation from this object.
/// </summary>
/// <param name="name">The annotation to remove.</param>
/// <param name="configurationSource">The configuration source of the annotation to be removed.</param>
/// <returns>The annotation that was removed.</returns>
public new virtual ConventionAnnotation? RemoveAnnotation(string name)
=> (ConventionAnnotation?)base.RemoveAnnotation(name);
public virtual ConventionAnnotation? RemoveAnnotation(string name, ConfigurationSource configurationSource)
{
var annotation = FindAnnotation(name);
return annotation == null
|| !configurationSource.Overrides(annotation.GetConfigurationSource())
? null
: (ConventionAnnotation?)base.RemoveAnnotation(name);
}

/// <inheritdoc />
protected override Annotation CreateAnnotation(string name, object? value)
Expand Down Expand Up @@ -201,8 +208,8 @@ IConventionAnnotation IConventionAnnotatable.AddAnnotation(string name, object?

/// <inheritdoc />
[DebuggerStepThrough]
IConventionAnnotation? IConventionAnnotatable.RemoveAnnotation(string name)
=> RemoveAnnotation(name);
IConventionAnnotation? IConventionAnnotatable.RemoveAnnotation(string name, bool fromDataAnnotation)
=> RemoveAnnotation(name, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);

/// <inheritdoc />
[DebuggerStepThrough]
Expand Down
8 changes: 7 additions & 1 deletion src/EFCore/Infrastructure/ModelValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -604,13 +604,19 @@ protected virtual void ValidateDiscriminatorValues(IEntityType rootEntityType)
continue;
}

var discriminatorValue = derivedType.GetDiscriminatorValue();
var discriminatorValue = derivedType[CoreAnnotationNames.DiscriminatorValue];
if (discriminatorValue == null)
{
throw new InvalidOperationException(
CoreStrings.NoDiscriminatorValue(derivedType.DisplayName()));
}

if (!discriminatorProperty.ClrType.IsInstanceOfType(discriminatorValue))
{
throw new InvalidOperationException(
CoreStrings.DiscriminatorValueIncompatible(discriminatorValue, discriminatorProperty.Name, discriminatorProperty.ClrType));
}

if (discriminatorValues.TryGetValue(discriminatorValue, out var duplicateEntityType))
{
throw new InvalidOperationException(
Expand Down
3 changes: 2 additions & 1 deletion src/EFCore/Metadata/IConventionAnnotatable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,9 @@ public interface IConventionAnnotatable : IReadOnlyAnnotatable
/// Removes the annotation with the given name from this object.
/// </summary>
/// <param name="name">The name of the annotation to remove.</param>
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns>The annotation that was removed.</returns>
IConventionAnnotation? RemoveAnnotation(string name);
IConventionAnnotation? RemoveAnnotation(string name, bool fromDataAnnotation = false);

/// <summary>
/// Gets the annotation with the given name, throwing if it does not exist.
Expand Down
7 changes: 4 additions & 3 deletions src/EFCore/Metadata/IConventionEntityType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,16 @@ public interface IConventionEntityType : IReadOnlyEntityType, IConventionTypeBas
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns>The configured value.</returns>
object? SetDiscriminatorValue(object? value, bool fromDataAnnotation = false)
=> SetAnnotation(CoreAnnotationNames.DiscriminatorValue, EntityType.CheckDiscriminatorValue(this, value), fromDataAnnotation)
=> SetAnnotation(CoreAnnotationNames.DiscriminatorValue, value, fromDataAnnotation)
?.Value;

/// <summary>
/// Removes the discriminator value for this entity type.
/// </summary>
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns>The removed discriminator value.</returns>
object? RemoveDiscriminatorValue()
=> RemoveAnnotation(CoreAnnotationNames.DiscriminatorValue)?.Value;
object? RemoveDiscriminatorValue(bool fromDataAnnotation = false)
=> RemoveAnnotation(CoreAnnotationNames.DiscriminatorValue, fromDataAnnotation)?.Value;

/// <summary>
/// Gets the <see cref="ConfigurationSource" /> for the discriminator value.
Expand Down
2 changes: 1 addition & 1 deletion src/EFCore/Metadata/IMutableEntityType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ void SetDiscriminatorMappingComplete(bool? complete)
/// </summary>
/// <param name="value">The value to set.</param>
void SetDiscriminatorValue(object? value)
=> SetAnnotation(CoreAnnotationNames.DiscriminatorValue, EntityType.CheckDiscriminatorValue(this, value));
=> SetAnnotation(CoreAnnotationNames.DiscriminatorValue, value);

/// <summary>
/// Removes the discriminator value for this entity type.
Expand Down
10 changes: 9 additions & 1 deletion src/EFCore/Metadata/IReadOnlyEntityType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,15 @@ bool GetIsDiscriminatorMappingComplete()
/// </summary>
/// <returns>The discriminator value for this entity type.</returns>
object? GetDiscriminatorValue()
=> this[CoreAnnotationNames.DiscriminatorValue];
{
var annotation = FindAnnotation(CoreAnnotationNames.DiscriminatorValue);
return annotation != null
? annotation.Value
: !ClrType.IsInstantiable()
|| (BaseType == null && GetDirectlyDerivedTypes().Count() == 0)
? null
: (object)ShortName();
}

/// <summary>
/// Gets all types in the model from which a given entity type derives, starting with the root.
Expand Down
50 changes: 17 additions & 33 deletions src/EFCore/Metadata/Internal/EntityType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.ComponentModel;
using System.Runtime.CompilerServices;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Internal;

Expand Down Expand Up @@ -3032,7 +3033,7 @@ public virtual IEnumerable<ServiceProperty> GetDerivedServiceProperties()
{
return Enumerable.Empty<IDictionary<string, object?>>();
}

List<IPropertyBase>? propertiesList = null;
Dictionary<string, IPropertyBase>? propertiesMap = null;
var data = new List<Dictionary<string, object?>>();
Expand All @@ -3056,7 +3057,7 @@ public virtual IEnumerable<ServiceProperty> GetDerivedServiceProperties()
{
continue;
}

ValueConverter? valueConverter = null;
if (providerValues
&& propertyBase is IProperty property
Expand Down Expand Up @@ -3347,12 +3348,12 @@ public virtual ChangeTrackingStrategy GetChangeTrackingStrategy()
if (((property == null && BaseType == null)
|| (property != null && !property.ClrType.IsInstanceOfType(((IReadOnlyEntityType)this).GetDiscriminatorValue()))))
{
((IMutableEntityType)this).RemoveDiscriminatorValue();
RemoveDiscriminatorValue(this, configurationSource);
if (BaseType == null)
{
foreach (var derivedType in GetDerivedTypes())
{
((IMutableEntityType)derivedType).RemoveDiscriminatorValue();
RemoveDiscriminatorValue(derivedType, configurationSource);
}
}
}
Expand All @@ -3361,6 +3362,18 @@ public virtual ChangeTrackingStrategy GetChangeTrackingStrategy()
== property?.Name
? property
: (Property?)((IReadOnlyEntityType)this).FindDiscriminatorProperty();

static void RemoveDiscriminatorValue(IReadOnlyEntityType entityType, ConfigurationSource configurationSource)
{
if (configurationSource is ConfigurationSource.Convention or ConfigurationSource.DataAnnotation)
{
((IConventionEntityType)entityType).RemoveDiscriminatorValue(configurationSource == ConfigurationSource.DataAnnotation);
}
else
{
((IMutableEntityType)entityType).RemoveDiscriminatorValue();
}
}
}

private void CheckDiscriminatorProperty(Property? property)
Expand Down Expand Up @@ -3395,35 +3408,6 @@ private void CheckDiscriminatorProperty(Property? property)
return (string?)this[CoreAnnotationNames.DiscriminatorProperty];
}

/// <summary>
/// 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.
/// </summary>
public static object? CheckDiscriminatorValue(IReadOnlyEntityType entityType, object? value)
{
if (value is null)
{
return value;
}

var discriminatorProperty = entityType.FindDiscriminatorProperty();
if (discriminatorProperty is null)
{
throw new InvalidOperationException(
CoreStrings.NoDiscriminatorForValue(entityType.DisplayName(), entityType.GetRootType().DisplayName()));
}

if (!discriminatorProperty.ClrType.IsInstanceOfType(value))
{
throw new InvalidOperationException(
CoreStrings.DiscriminatorValueIncompatible(value, discriminatorProperty.Name, discriminatorProperty.ClrType));
}

return value;
}

/// <summary>
/// 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
Expand Down
16 changes: 4 additions & 12 deletions src/EFCore/Properties/CoreStrings.Designer.cs

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

Loading