diff --git a/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs b/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs
index 557407a0b63..7110d784db8 100644
--- a/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs
@@ -678,6 +678,15 @@ private void Create(
property.DeclaringEntityType.ShortName(), property.Name, nameof(PropertyBuilder.HasConversion)));
}
+ var providerValueComparerType = (Type?)property[CoreAnnotationNames.ProviderValueComparerType];
+ if (providerValueComparerType == null
+ && property[CoreAnnotationNames.ProviderValueComparer] != null)
+ {
+ throw new InvalidOperationException(
+ DesignStrings.CompiledModelValueComparer(
+ property.DeclaringEntityType.ShortName(), property.Name, nameof(PropertyBuilder.HasConversion)));
+ }
+
var valueConverterType = (Type?)property[CoreAnnotationNames.ValueConverterType];
if (valueConverterType == null
&& property.GetValueConverter() != null)
@@ -809,6 +818,16 @@ private void Create(
.Append("()");
}
+ if (providerValueComparerType != null)
+ {
+ AddNamespace(providerValueComparerType, parameters.Namespaces);
+
+ mainBuilder.AppendLine(",")
+ .Append("providerValueComparer: new ")
+ .Append(_code.Reference(providerValueComparerType))
+ .Append("()");
+ }
+
mainBuilder
.AppendLine(");")
.DecrementIndent();
diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
index a284accdf5a..8711ffdbf8b 100644
--- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
+++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
@@ -1318,7 +1318,7 @@ protected override void ValidateInheritanceMapping(
var mappingStrategy = (string?)entityType[RelationalAnnotationNames.MappingStrategy];
if (mappingStrategy != null)
{
- ValidateMappingStrategy(mappingStrategy, entityType);
+ ValidateMappingStrategy(entityType, mappingStrategy);
var storeObject = entityType.GetSchemaQualifiedTableName()
?? entityType.GetSchemaQualifiedViewName()
?? entityType.GetFunctionName();
@@ -1389,9 +1389,9 @@ protected override void ValidateInheritanceMapping(
///
/// Validates that the given mapping strategy is supported
///
- /// The mapping strategy.
/// The entity type.
- protected virtual void ValidateMappingStrategy(string? mappingStrategy, IEntityType entityType)
+ /// The mapping strategy.
+ protected virtual void ValidateMappingStrategy(IEntityType entityType, string? mappingStrategy)
{
switch (mappingStrategy)
{
diff --git a/src/EFCore.Relational/Metadata/IColumn.cs b/src/EFCore.Relational/Metadata/IColumn.cs
index 4620f09db9d..99685f72dc3 100644
--- a/src/EFCore.Relational/Metadata/IColumn.cs
+++ b/src/EFCore.Relational/Metadata/IColumn.cs
@@ -147,6 +147,14 @@ public virtual string? Collation
=> PropertyMappings.First().Property
.GetCollation(StoreObjectIdentifier.Table(Table.Name, Table.Schema));
+ ///
+ /// Gets the for this column.
+ ///
+ /// The comparer.
+ public virtual ValueComparer ProviderValueComparer
+ => PropertyMappings.First().Property
+ .GetProviderValueComparer();
+
///
/// Returns the property mapping for the given entity type.
///
diff --git a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
index cf9a4c33d1e..941add0b3ad 100644
--- a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
+++ b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
@@ -2062,7 +2062,7 @@ protected virtual void DiffData(
var sourceValue = sourceColumnModification.OriginalValue;
var targetValue = targetColumnModification.Value;
- var comparer = targetMapping.TypeMapping.ProviderValueComparer;
+ var comparer = targetColumn.ProviderValueComparer;
if (sourceColumn.ProviderClrType == targetColumn.ProviderClrType
&& comparer.Equals(sourceValue, targetValue))
{
diff --git a/src/EFCore.Relational/Storage/RelationalGeometryTypeMapping.cs b/src/EFCore.Relational/Storage/RelationalGeometryTypeMapping.cs
index 8bd9dd44c51..f145f044a28 100644
--- a/src/EFCore.Relational/Storage/RelationalGeometryTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/RelationalGeometryTypeMapping.cs
@@ -27,7 +27,6 @@ protected RelationalGeometryTypeMapping(
: base(CreateRelationalTypeMappingParameters(storeType))
{
SpatialConverter = converter;
- SetProviderValueComparer();
}
///
@@ -38,25 +37,19 @@ protected RelationalGeometryTypeMapping(
protected RelationalGeometryTypeMapping(
RelationalTypeMappingParameters parameters,
ValueConverter? converter)
- : base(parameters)
+ : base(parameters.WithCoreParameters(parameters.CoreParameters with
+ {
+ ProviderValueComparer = parameters.CoreParameters.ProviderValueComparer
+ ?? CreateProviderValueComparer(parameters.CoreParameters.Converter?.ProviderClrType ?? parameters.CoreParameters.ClrType)
+ }))
{
SpatialConverter = converter;
- SetProviderValueComparer();
}
- private void SetProviderValueComparer()
- {
- var providerType = Converter?.ProviderClrType ?? ClrType;
- if (providerType.IsAssignableTo(typeof(TGeometry)))
- {
- ProviderValueComparer = (ValueComparer)Activator.CreateInstance(typeof(GeometryValueComparer<>).MakeGenericType(providerType))!;
- }
- }
-
- ///
- /// The underlying Geometry converter.
- ///
- protected virtual ValueConverter? SpatialConverter { get; }
+ private static ValueComparer? CreateProviderValueComparer(Type providerType)
+ => providerType.IsAssignableTo(typeof(TGeometry))
+ ? (ValueComparer)Activator.CreateInstance(typeof(GeometryValueComparer<>).MakeGenericType(providerType))!
+ : null;
private static RelationalTypeMappingParameters CreateRelationalTypeMappingParameters(string storeType)
{
@@ -67,10 +60,16 @@ private static RelationalTypeMappingParameters CreateRelationalTypeMappingParame
typeof(TGeometry),
null,
comparer,
- comparer),
+ comparer,
+ CreateProviderValueComparer(typeof(TGeometry))),
storeType);
}
+ ///
+ /// The underlying Geometry converter.
+ ///
+ protected virtual ValueConverter? SpatialConverter { get; }
+
///
/// Creates a with the appropriate type information configured.
///
diff --git a/src/EFCore.Relational/Storage/RelationalTypeMapping.cs b/src/EFCore.Relational/Storage/RelationalTypeMapping.cs
index aa41dff067d..d223502f13d 100644
--- a/src/EFCore.Relational/Storage/RelationalTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/RelationalTypeMapping.cs
@@ -109,6 +109,24 @@ public RelationalTypeMappingParameters(
///
public StoreTypePostfix StoreTypePostfix { get; }
+ ///
+ /// Creates a new parameter object with the given
+ /// core parameters.
+ ///
+ /// Parameters for the base class.
+ /// The new parameter object.
+ public RelationalTypeMappingParameters WithCoreParameters(in CoreTypeMappingParameters coreParameters)
+ => new(
+ coreParameters,
+ StoreType,
+ StoreTypePostfix,
+ DbType,
+ Unicode,
+ Size,
+ FixedLength,
+ Precision,
+ Scale);
+
///
/// Creates a new parameter object with the given
/// mapping info.
@@ -261,8 +279,6 @@ protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters p
=> this;
}
- private ValueComparer? _providerValueComparer;
-
///
/// Initializes a new instance of the class.
///
@@ -380,19 +396,6 @@ public virtual bool IsFixedLength
protected virtual string SqlLiteralFormatString
=> "{0}";
- ///
- /// A for the provider CLR type values.
- ///
- public virtual ValueComparer ProviderValueComparer
- {
- get => NonCapturingLazyInitializer.EnsureInitialized(
- ref _providerValueComparer,
- this,
- static c => ValueComparer.CreateDefault(c.Converter?.ProviderClrType ?? c.ClrType, favorStructuralComparisons: true));
-
- protected set => _providerValueComparer = value;
- }
-
///
/// Creates a copy of this mapping.
///
diff --git a/src/EFCore.Relational/Update/Internal/CommandBatchPreparer.cs b/src/EFCore.Relational/Update/Internal/CommandBatchPreparer.cs
index f062ee0e32f..05e355093b2 100644
--- a/src/EFCore.Relational/Update/Internal/CommandBatchPreparer.cs
+++ b/src/EFCore.Relational/Update/Internal/CommandBatchPreparer.cs
@@ -612,7 +612,7 @@ private static bool IsModified(IReadOnlyList columns, IReadOnlyModifica
var column = columns[columnIndex];
object? originalValue = null;
object? currentValue = null;
- RelationalTypeMapping? typeMapping = null;
+ ValueComparer? providerValueComparer = null;
for (var entryIndex = 0; entryIndex < command.Entries.Count; entryIndex++)
{
var entry = command.Entries[entryIndex];
@@ -641,12 +641,12 @@ private static bool IsModified(IReadOnlyList columns, IReadOnlyModifica
break;
}
- typeMapping = columnMapping!.TypeMapping;
+ providerValueComparer = property.GetProviderValueComparer();
}
}
- if (typeMapping != null
- && !typeMapping.ProviderValueComparer.Equals(originalValue, currentValue))
+ if (providerValueComparer != null
+ && !providerValueComparer.Equals(originalValue, currentValue))
{
return true;
}
diff --git a/src/EFCore.Relational/Update/Internal/CompositeRowValueFactory.cs b/src/EFCore.Relational/Update/Internal/CompositeRowValueFactory.cs
index 348495ac8ad..c4ab62efb5f 100644
--- a/src/EFCore.Relational/Update/Internal/CompositeRowValueFactory.cs
+++ b/src/EFCore.Relational/Update/Internal/CompositeRowValueFactory.cs
@@ -150,7 +150,7 @@ public virtual bool TryCreateDependentKeyValue(IReadOnlyModificationCommand comm
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected static IEqualityComparer