diff --git a/src/EFCore.Cosmos/ChangeTracking/Internal/ListComparer.cs b/src/EFCore.Cosmos/ChangeTracking/Internal/ListComparer.cs index dd948a025f4..82c81996c73 100644 --- a/src/EFCore.Cosmos/ChangeTracking/Internal/ListComparer.cs +++ b/src/EFCore.Cosmos/ChangeTracking/Internal/ListComparer.cs @@ -77,13 +77,8 @@ private static int GetHashCode(TCollection source, ValueComparer eleme return hash.ToHashCode(); } - private static TCollection? Snapshot(TCollection? source, ValueComparer elementComparer, bool readOnly) + private static TCollection Snapshot(TCollection source, ValueComparer elementComparer, bool readOnly) { - if (source is null) - { - return null; - } - if (readOnly) { return source; @@ -92,7 +87,7 @@ private static int GetHashCode(TCollection source, ValueComparer eleme var snapshot = new List(((IReadOnlyList)source).Count); foreach (var e in source) { - snapshot.Add(elementComparer.Snapshot(e)!); + snapshot.Add(e is null ? default! : elementComparer.Snapshot(e)); } return (TCollection)(object)snapshot; diff --git a/src/EFCore.Cosmos/ChangeTracking/Internal/NullableListComparer.cs b/src/EFCore.Cosmos/ChangeTracking/Internal/NullableListComparer.cs index 83d8ad041f0..c00962a5db0 100644 --- a/src/EFCore.Cosmos/ChangeTracking/Internal/NullableListComparer.cs +++ b/src/EFCore.Cosmos/ChangeTracking/Internal/NullableListComparer.cs @@ -89,13 +89,8 @@ private static int GetHashCode(TCollection source, ValueComparer eleme return hash.ToHashCode(); } - private static TCollection? Snapshot(TCollection? source, ValueComparer elementComparer, bool readOnly) + private static TCollection Snapshot(TCollection source, ValueComparer elementComparer, bool readOnly) { - if (source is null) - { - return null; - } - if (readOnly) { return source; diff --git a/src/EFCore.Cosmos/ChangeTracking/Internal/NullableSingleDimensionalArrayComparer.cs b/src/EFCore.Cosmos/ChangeTracking/Internal/NullableSingleDimensionalArrayComparer.cs index 3129542d108..8273a71d323 100644 --- a/src/EFCore.Cosmos/ChangeTracking/Internal/NullableSingleDimensionalArrayComparer.cs +++ b/src/EFCore.Cosmos/ChangeTracking/Internal/NullableSingleDimensionalArrayComparer.cs @@ -88,13 +88,8 @@ private static int GetHashCode(TElement?[] source, ValueComparer eleme } [return: NotNullIfNotNull("source")] - private static TElement?[]? Snapshot(TElement?[]? source, ValueComparer elementComparer) + private static TElement?[] Snapshot(TElement?[] source, ValueComparer elementComparer) { - if (source is null) - { - return null; - } - var snapshot = new TElement?[source.Length]; for (var i = 0; i < source.Length; i++) { diff --git a/src/EFCore.Cosmos/ChangeTracking/Internal/NullableStringDictionaryComparer.cs b/src/EFCore.Cosmos/ChangeTracking/Internal/NullableStringDictionaryComparer.cs index ca54128ef2d..532a523be8e 100644 --- a/src/EFCore.Cosmos/ChangeTracking/Internal/NullableStringDictionaryComparer.cs +++ b/src/EFCore.Cosmos/ChangeTracking/Internal/NullableStringDictionaryComparer.cs @@ -95,13 +95,8 @@ private static int GetHashCode(TCollection source, ValueComparer eleme return hash.ToHashCode(); } - private static TCollection? Snapshot(TCollection? source, ValueComparer elementComparer, bool readOnly) + private static TCollection Snapshot(TCollection source, ValueComparer elementComparer, bool readOnly) { - if (source is null) - { - return null; - } - if (readOnly) { return source; diff --git a/src/EFCore.Cosmos/ChangeTracking/Internal/SingleDimensionalArrayComparer.cs b/src/EFCore.Cosmos/ChangeTracking/Internal/SingleDimensionalArrayComparer.cs index 10507be7281..6031a6cceb6 100644 --- a/src/EFCore.Cosmos/ChangeTracking/Internal/SingleDimensionalArrayComparer.cs +++ b/src/EFCore.Cosmos/ChangeTracking/Internal/SingleDimensionalArrayComparer.cs @@ -76,17 +76,13 @@ private static int GetHashCode(TElement[] source, ValueComparer elemen } [return: NotNullIfNotNull("source")] - private static TElement[]? Snapshot(TElement[]? source, ValueComparer elementComparer) + private static TElement[] Snapshot(TElement[] source, ValueComparer elementComparer) { - if (source is null) - { - return null; - } - var snapshot = new TElement[source.Length]; for (var i = 0; i < source.Length; i++) { - snapshot[i] = elementComparer.Snapshot(source[i])!; + var element = source[i]; + snapshot[i] = element is null ? default! : elementComparer.Snapshot(source[i]); } return snapshot; } diff --git a/src/EFCore.Cosmos/ChangeTracking/Internal/StringDictionaryComparer.cs b/src/EFCore.Cosmos/ChangeTracking/Internal/StringDictionaryComparer.cs index 9c0ecaa2936..4c8ceffc221 100644 --- a/src/EFCore.Cosmos/ChangeTracking/Internal/StringDictionaryComparer.cs +++ b/src/EFCore.Cosmos/ChangeTracking/Internal/StringDictionaryComparer.cs @@ -79,13 +79,8 @@ private static int GetHashCode(TCollection source, ValueComparer eleme return hash.ToHashCode(); } - private static TCollection? Snapshot(TCollection? source, ValueComparer elementComparer, bool readOnly) + private static TCollection Snapshot(TCollection source, ValueComparer elementComparer, bool readOnly) { - if (source is null) - { - return null; - } - if (readOnly) { return source; @@ -94,7 +89,7 @@ private static int GetHashCode(TCollection source, ValueComparer eleme var snapshot = new Dictionary(((IReadOnlyDictionary)source).Count); foreach (var e in source) { - snapshot.Add(e.Key, elementComparer.Snapshot(e.Value)!); + snapshot.Add(e.Key, e.Value is null ? default! : elementComparer.Snapshot(e.Value)); } return (TCollection)(object)snapshot; diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs index 99fe08b960d..cb8d79987ab 100644 --- a/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs +++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs @@ -52,7 +52,7 @@ private readonly SqlServerByteArrayTypeMapping _rowversion comparer: new ValueComparer( (v1, v2) => StructuralComparisons.StructuralEqualityComparer.Equals(v1, v2), v => StructuralComparisons.StructuralEqualityComparer.GetHashCode(v), - v => v == null ? null : v.ToArray()), + v => v.ToArray()), storeTypePostfix: StoreTypePostfix.None); private readonly IntTypeMapping _int diff --git a/src/EFCore/ChangeTracking/ArrayStructuralComparer.cs b/src/EFCore/ChangeTracking/ArrayStructuralComparer.cs index d8f5b98d4d8..7b2a33aa74d 100644 --- a/src/EFCore/ChangeTracking/ArrayStructuralComparer.cs +++ b/src/EFCore/ChangeTracking/ArrayStructuralComparer.cs @@ -24,7 +24,7 @@ public ArrayStructuralComparer() : base( CreateDefaultEqualsExpression(), CreateDefaultHashCodeExpression(favorStructuralComparisons: true), - v => v == null ? null : v.ToArray()) + v => v.ToArray()) { } } diff --git a/src/EFCore/ChangeTracking/GeometryValueComparer.cs b/src/EFCore/ChangeTracking/GeometryValueComparer.cs index 362e3016684..98e7371ad4f 100644 --- a/src/EFCore/ChangeTracking/GeometryValueComparer.cs +++ b/src/EFCore/ChangeTracking/GeometryValueComparer.cs @@ -58,7 +58,7 @@ public GeometryValueComparer() right); } - private static Expression> GetSnapshotExpression() + private static Expression> GetSnapshotExpression() { var instance = Expression.Parameter(typeof(TGeometry), "instance"); @@ -71,7 +71,7 @@ public GeometryValueComparer() body = Expression.Convert(body, typeof(TGeometry)); } - return Expression.Lambda>(body, instance); + return Expression.Lambda>(body, instance); } } } diff --git a/src/EFCore/ChangeTracking/Internal/SimplePrincipalKeyValueFactory.cs b/src/EFCore/ChangeTracking/Internal/SimplePrincipalKeyValueFactory.cs index b0096b4fe51..839b54dd400 100644 --- a/src/EFCore/ChangeTracking/Internal/SimplePrincipalKeyValueFactory.cs +++ b/src/EFCore/ChangeTracking/Internal/SimplePrincipalKeyValueFactory.cs @@ -148,7 +148,7 @@ public NoNullsCustomEqualityComparer(ValueComparer comparer) public bool Equals(TKey? x, TKey? y) => _equals(x, y); - public int GetHashCode(TKey obj) + public int GetHashCode([DisallowNull] TKey obj) => _hashCode(obj); } } diff --git a/src/EFCore/ChangeTracking/Internal/ValueComparerExtensions.cs b/src/EFCore/ChangeTracking/Internal/ValueComparerExtensions.cs index f2689a988f1..35bd8e278f5 100644 --- a/src/EFCore/ChangeTracking/Internal/ValueComparerExtensions.cs +++ b/src/EFCore/ChangeTracking/Internal/ValueComparerExtensions.cs @@ -58,7 +58,7 @@ public NonNullNullableValueComparer( : base( (Expression>)equalsExpression, (Expression>)hashCodeExpression, - (Expression>)snapshotExpression) + (Expression>)snapshotExpression) { } } diff --git a/src/EFCore/ChangeTracking/ValueComparer.cs b/src/EFCore/ChangeTracking/ValueComparer.cs index 699ee5066e7..286907f6953 100644 --- a/src/EFCore/ChangeTracking/ValueComparer.cs +++ b/src/EFCore/ChangeTracking/ValueComparer.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -106,6 +107,7 @@ protected ValueComparer( /// /// The instance. /// The snapshot. + [return: NotNullIfNotNull("instance")] public abstract object? Snapshot(object? instance); /// @@ -196,31 +198,31 @@ public virtual Expression ExtractSnapshotBody(Expression expression) /// The . public static ValueComparer CreateDefault(Type type, bool favorStructuralComparisons) { - var nonNullabletype = type.UnwrapNullableType(); + var nonNullableType = type.UnwrapNullableType(); // The equality operator returns false for NaNs, but the Equals methods returns true - if (nonNullabletype == typeof(double)) + if (nonNullableType == typeof(double)) { return new DefaultDoubleValueComparer(favorStructuralComparisons); } - if (nonNullabletype == typeof(float)) + if (nonNullableType == typeof(float)) { return new DefaultFloatValueComparer(favorStructuralComparisons); } - if (nonNullabletype == typeof(DateTimeOffset)) + if (nonNullableType == typeof(DateTimeOffset)) { return new DefaultDateTimeOffsetValueComparer(favorStructuralComparisons); } - var comparerType = nonNullabletype.IsInteger() - || nonNullabletype == typeof(decimal) - || nonNullabletype == typeof(bool) - || nonNullabletype == typeof(string) - || nonNullabletype == typeof(DateTime) - || nonNullabletype == typeof(Guid) - || nonNullabletype == typeof(TimeSpan) + var comparerType = nonNullableType.IsInteger() + || nonNullableType == typeof(decimal) + || nonNullableType == typeof(bool) + || nonNullableType == typeof(string) + || nonNullableType == typeof(DateTime) + || nonNullableType == typeof(Guid) + || nonNullableType == typeof(TimeSpan) ? typeof(DefaultValueComparer<>) : typeof(ValueComparer<>); @@ -253,7 +255,7 @@ public override Expression ExtractSnapshotBody(Expression expression) public override object? Snapshot(object? instance) => instance; - public override T? Snapshot(T? instance) + public override T Snapshot(T instance) => instance; } diff --git a/src/EFCore/ChangeTracking/ValueComparer`.cs b/src/EFCore/ChangeTracking/ValueComparer`.cs index dfe32711be3..a0a5ba75044 100644 --- a/src/EFCore/ChangeTracking/ValueComparer`.cs +++ b/src/EFCore/ChangeTracking/ValueComparer`.cs @@ -33,7 +33,7 @@ public class ValueComparer : ValueComparer, IEqualityComparer { private Func? _equals; private Func? _hashCode; - private Func? _snapshot; + private Func? _snapshot; /// /// Creates a new with a default comparison @@ -82,7 +82,7 @@ public ValueComparer( public ValueComparer( Expression> equalsExpression, Expression> hashCodeExpression, - Expression> snapshotExpression) + Expression> snapshotExpression) : base(equalsExpression, hashCodeExpression, snapshotExpression) { } @@ -161,7 +161,7 @@ public ValueComparer( /// Creates an expression for creating a snapshot of a value. /// /// The snapshot expression. - protected static Expression> CreateDefaultSnapshotExpression(bool favorStructuralComparisons) + protected static Expression> CreateDefaultSnapshotExpression(bool favorStructuralComparisons) { if (!favorStructuralComparisons || !typeof(T).IsArray) @@ -178,7 +178,7 @@ public ValueComparer( // var destination = new T[length]; // Array.Copy(source, destination, length); // return destination; - return Expression.Lambda>( + return Expression.Lambda>( Expression.Block( new[] { lengthVariable, destinationVariable }, Expression.Assign( @@ -257,8 +257,8 @@ public override bool Equals(object? left, object? right) /// /// The instance. /// The hash code. - public override int GetHashCode(object? instance) - => instance == null ? 0 : GetHashCode((T)instance); + public override int GetHashCode(object instance) + => instance is null ? 0 : GetHashCode((T)instance); /// /// Compares the two instances to determine if they are equal. @@ -293,7 +293,7 @@ public virtual int GetHashCode(T instance) /// The instance. /// The snapshot. public override object? Snapshot(object? instance) - => instance == null ? null : Snapshot((T?)instance); + => instance == null ? null : Snapshot((T)instance); /// /// @@ -308,7 +308,7 @@ public virtual int GetHashCode(T instance) /// /// The instance. /// The snapshot. - public virtual T? Snapshot(T? instance) + public virtual T Snapshot(T instance) => NonCapturingLazyInitializer.EnsureInitialized( ref _snapshot, this, static c => c.SnapshotExpression.Compile())(instance); @@ -341,7 +341,7 @@ public override Type Type /// reference. /// /// - public new virtual Expression> SnapshotExpression - => (Expression>)base.SnapshotExpression; + public new virtual Expression> SnapshotExpression + => (Expression>)base.SnapshotExpression; } } diff --git a/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs index bf13a24ca0e..da3fa9694eb 100644 --- a/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs @@ -606,14 +606,13 @@ await Can_add_update_delete_with_collection( public async Task Can_add_update_delete_with_nested_collections() { await Can_add_update_delete_with_collection( - new List> { new List { 1, 2 } }, + new List> { new() { 1, 2 } }, c => { c.Collection.Clear(); c.Collection.Add(new List { 3 }); }, - new List> { new List { 3 } }); - + new List> { new() { 3 } }); await Can_add_update_delete_with_collection>( new List(), c => @@ -622,30 +621,29 @@ await Can_add_update_delete_with_collection( c.Collection.Add(null); }, new List { new byte?[] { 3, null }, null }); - await Can_add_update_delete_with_collection>>( - new Dictionary[] { new Dictionary { { "1", null } } }, + new Dictionary[] { new() { { "1", null } } }, c => { var dictionary = c.Collection[0]["3"] = "2"; }, - new List> { new Dictionary { { "1", null }, { "3", "2" } } }); + new List> { new() { { "1", null }, { "3", "2" } } }); await Can_add_update_delete_with_collection( - new List[] { new List { 1f }, new List { 2 } }, + new List[] { new() { 1f }, new() { 2 } }, c => { c.Collection[1][0] = 3f; }, - new List[] { new List { 1f }, new List { 3f } }); + new List[] { new() { 1f }, new() { 3f } }); await Can_add_update_delete_with_collection( - new decimal?[][] { new decimal?[] { 1, null } }, + new[] { new decimal?[] { 1, null } }, c => { c.Collection[0][1] = 3; }, - new decimal?[][] { new decimal?[] { 1, 3 } }); + new[] { new decimal?[] { 1, 3 } }); await Can_add_update_delete_with_collection( new Dictionary> { { "1", new List { 1 } } }, diff --git a/test/EFCore.Tests/Storage/ValueComparerTest.cs b/test/EFCore.Tests/Storage/ValueComparerTest.cs index f8f61a4b305..fb3b6c27311 100644 --- a/test/EFCore.Tests/Storage/ValueComparerTest.cs +++ b/test/EFCore.Tests/Storage/ValueComparerTest.cs @@ -85,7 +85,6 @@ private static ValueComparer CompareTest(Type type, object value1, object value2 Assert.False(comparer.Equals(null, value2)); Assert.True(comparer.Equals(null, null)); - Assert.Equal(0, comparer.GetHashCode(null)); Assert.Equal(hashCode ?? value1.GetHashCode(), comparer.GetHashCode(value1)); var keyComparer = (ValueComparer)Activator.CreateInstance(typeof(ValueComparer<>).MakeGenericType(type), new object[] { true }); @@ -102,7 +101,6 @@ private static ValueComparer CompareTest(Type type, object value1, object value2 Assert.False(keyComparer.Equals(null, value2)); Assert.True(keyComparer.Equals(null, null)); - Assert.Equal(0, keyComparer.GetHashCode(null)); Assert.Equal(hashCode ?? value1.GetHashCode(), keyComparer.GetHashCode(value1)); return comparer;