diff --git a/src/EFCore.Relational/Storage/DoubleTypeMapping.cs b/src/EFCore.Relational/Storage/DoubleTypeMapping.cs index 36e89971154..a3d02de3d34 100644 --- a/src/EFCore.Relational/Storage/DoubleTypeMapping.cs +++ b/src/EFCore.Relational/Storage/DoubleTypeMapping.cs @@ -4,6 +4,7 @@ using System; using System.Data; using System.Globalization; +using Microsoft.EntityFrameworkCore.ChangeTracking; using JetBrains.Annotations; namespace Microsoft.EntityFrameworkCore.Storage @@ -27,7 +28,10 @@ public class DoubleTypeMapping : RelationalTypeMapping public DoubleTypeMapping( [NotNull] string storeType, DbType? dbType = null) - : base(storeType, typeof(double), dbType) + : base( + new RelationalTypeMappingParameters( + new CoreTypeMappingParameters(typeof(double), comparer: new DoubleValueComparer()), + storeType, StoreTypePostfix.None, dbType, unicode: false, size: null, fixedLength: false, precision: null, scale: null)) { } diff --git a/src/EFCore.Relational/Storage/FloatTypeMapping.cs b/src/EFCore.Relational/Storage/FloatTypeMapping.cs index 92833456ba5..4a0c83df974 100644 --- a/src/EFCore.Relational/Storage/FloatTypeMapping.cs +++ b/src/EFCore.Relational/Storage/FloatTypeMapping.cs @@ -5,6 +5,7 @@ using System.Data; using System.Globalization; using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.ChangeTracking; namespace Microsoft.EntityFrameworkCore.Storage { @@ -27,7 +28,10 @@ public class FloatTypeMapping : RelationalTypeMapping public FloatTypeMapping( [NotNull] string storeType, DbType? dbType = null) - : base(storeType, typeof(float), dbType) + : base( + new RelationalTypeMappingParameters( + new CoreTypeMappingParameters(typeof(float), comparer: new FloatValueComparer()), + storeType, StoreTypePostfix.None, dbType, unicode: false, size: null, fixedLength: false, precision: null, scale: null)) { } diff --git a/src/EFCore/ChangeTracking/ArrayStructuralComparer.cs b/src/EFCore/ChangeTracking/ArrayStructuralComparer.cs index 3f5484e92e3..a3937a904d3 100644 --- a/src/EFCore/ChangeTracking/ArrayStructuralComparer.cs +++ b/src/EFCore/ChangeTracking/ArrayStructuralComparer.cs @@ -7,8 +7,8 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking { /// /// - /// Specifies value snapshotting and comparison for arrays where each element is compared - /// a new array is constructed when snapshotting. + /// Specifies value comparison for arrays where each element pair is compared. + /// A new array is constructed when snapshotting. /// /// /// The array element type. diff --git a/src/EFCore/ChangeTracking/DoubleValueComparer.cs b/src/EFCore/ChangeTracking/DoubleValueComparer.cs new file mode 100644 index 00000000000..3c1d630eea6 --- /dev/null +++ b/src/EFCore/ChangeTracking/DoubleValueComparer.cs @@ -0,0 +1,25 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq.Expressions; +using JetBrains.Annotations; + +namespace Microsoft.EntityFrameworkCore.ChangeTracking +{ + /// + /// Defines value comparison for which correctly takes + /// into account. + /// + public class DoubleValueComparer : ValueComparer + { + /// + /// Initializes a new instance of the class. + /// + public DoubleValueComparer() : base( + (x, y) => double.IsNaN(x) ? double.IsNaN(y) : x.Equals(y), + d => d.GetHashCode()) + { + } + } +} diff --git a/src/EFCore/ChangeTracking/FloatValueComparer.cs b/src/EFCore/ChangeTracking/FloatValueComparer.cs new file mode 100644 index 00000000000..89fb828110b --- /dev/null +++ b/src/EFCore/ChangeTracking/FloatValueComparer.cs @@ -0,0 +1,25 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq.Expressions; +using JetBrains.Annotations; + +namespace Microsoft.EntityFrameworkCore.ChangeTracking +{ + /// + /// Defines value comparison for which correctly takes + /// into account. + /// + public class FloatValueComparer : ValueComparer + { + /// + /// Initializes a new instance of the class. + /// + public FloatValueComparer() : base( + (x, y) => float.IsNaN(x) ? float.IsNaN(y) : x.Equals(y), + d => d.GetHashCode()) + { + } + } +} diff --git a/test/EFCore.Relational.Tests/Storage/RelationalTypeMappingTest.cs b/test/EFCore.Relational.Tests/Storage/RelationalTypeMappingTest.cs index 7ba4084749e..e1fe8b92e5e 100644 --- a/test/EFCore.Relational.Tests/Storage/RelationalTypeMappingTest.cs +++ b/test/EFCore.Relational.Tests/Storage/RelationalTypeMappingTest.cs @@ -529,6 +529,26 @@ public virtual void UShort_literal_generated_correctly() Test_GenerateSqlLiteral_helper(typeMapping, ushort.MaxValue, "65535"); } + [ConditionalFact] + public virtual void Double_value_comparer_handles_NaN() + { + var typeMapping = new DoubleTypeMapping("double precision", DbType.Double); + + Assert.True(typeMapping.Comparer.Equals(3.0, 3.0)); + Assert.True(typeMapping.Comparer.Equals(double.NaN, double.NaN)); + Assert.False(typeMapping.Comparer.Equals(3.0, double.NaN)); + } + + [ConditionalFact] + public virtual void Float_value_comparer_handles_NaN() + { + var typeMapping = new FloatTypeMapping("float", DbType.Single); + + Assert.True(typeMapping.Comparer.Equals(3.0f, 3.0f)); + Assert.True(typeMapping.Comparer.Equals(float.NaN, float.NaN)); + Assert.False(typeMapping.Comparer.Equals(3.0f, float.NaN)); + } + [ConditionalFact] public virtual void Primary_key_type_mapping_is_picked_up_by_FK_without_going_through_store_type() {