diff --git a/src/EFCore.Cosmos/ChangeTracking/Internal/ListComparer.cs b/src/EFCore.Cosmos/ChangeTracking/Internal/ListComparer.cs
deleted file mode 100644
index 576b8d33c26..00000000000
--- a/src/EFCore.Cosmos/ChangeTracking/Internal/ListComparer.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace Microsoft.EntityFrameworkCore.Cosmos.ChangeTracking.Internal;
-
-///
-/// 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.
-///
-public sealed class ListComparer : ValueComparer
- where TCollection : class, IEnumerable
-{
- ///
- /// 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.
- ///
- public ListComparer(ValueComparer elementComparer, bool readOnly)
- : base(
- (a, b) => Compare(a, b, (ValueComparer)elementComparer),
- o => GetHashCode(o, (ValueComparer)elementComparer),
- source => Snapshot(source, (ValueComparer)elementComparer, readOnly))
- {
- }
-
- ///
- /// 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.
- ///
- public override Type Type
- => typeof(TCollection);
-
- private static bool Compare(TCollection? a, TCollection? b, ValueComparer elementComparer)
- {
- if (a is not IReadOnlyList aList)
- {
- return b is not IReadOnlyList;
- }
-
- if (b is not IReadOnlyList bList || aList.Count != bList.Count)
- {
- return false;
- }
-
- if (ReferenceEquals(aList, bList))
- {
- return true;
- }
-
- for (var i = 0; i < aList.Count; i++)
- {
- if (!elementComparer.Equals(aList[i], bList[i]))
- {
- return false;
- }
- }
-
- return true;
- }
-
- private static int GetHashCode(TCollection source, ValueComparer elementComparer)
- {
- var hash = new HashCode();
- foreach (var el in source)
- {
- hash.Add(el, elementComparer);
- }
-
- return hash.ToHashCode();
- }
-
- private static TCollection Snapshot(TCollection source, ValueComparer elementComparer, bool readOnly)
- {
- if (readOnly)
- {
- return source;
- }
-
- var snapshot = new List(((IReadOnlyList)source).Count);
- foreach (var e in source)
- {
- 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
deleted file mode 100644
index 80fcb55b72f..00000000000
--- a/src/EFCore.Cosmos/ChangeTracking/Internal/NullableListComparer.cs
+++ /dev/null
@@ -1,105 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace Microsoft.EntityFrameworkCore.Cosmos.ChangeTracking.Internal;
-
-///
-/// 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.
-///
-public sealed class NullableListComparer : ValueComparer
- where TCollection : class, IEnumerable
- where TElement : struct
-{
- ///
- /// 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.
- ///
- public NullableListComparer(ValueComparer elementComparer, bool readOnly)
- : base(
- (a, b) => Compare(a, b, (ValueComparer)elementComparer),
- o => GetHashCode(o, (ValueComparer)elementComparer),
- source => Snapshot(source, (ValueComparer)elementComparer, readOnly))
- {
- }
-
- ///
- /// 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.
- ///
- public override Type Type
- => typeof(TCollection);
-
- private static bool Compare(TCollection? a, TCollection? b, ValueComparer elementComparer)
- {
- if (a is not IReadOnlyList aList)
- {
- return b is not IReadOnlyList;
- }
-
- if (b is not IReadOnlyList bList || aList.Count != bList.Count)
- {
- return false;
- }
-
- if (ReferenceEquals(aList, bList))
- {
- return true;
- }
-
- for (var i = 0; i < aList.Count; i++)
- {
- var (aElement, bElement) = (aList[i], bList[i]);
- if (aElement is null)
- {
- if (bElement is null)
- {
- continue;
- }
-
- return false;
- }
-
- if (bElement is null || !elementComparer.Equals(aElement, bElement))
- {
- return false;
- }
- }
-
- return true;
- }
-
- private static int GetHashCode(TCollection source, ValueComparer elementComparer)
- {
- var nullableEqualityComparer = new NullableEqualityComparer(elementComparer);
- var hash = new HashCode();
- foreach (var el in source)
- {
- hash.Add(el, nullableEqualityComparer);
- }
-
- return hash.ToHashCode();
- }
-
- private static TCollection Snapshot(TCollection source, ValueComparer elementComparer, bool readOnly)
- {
- if (readOnly)
- {
- return source;
- }
-
- var snapshot = new List(((IReadOnlyList)source).Count);
- foreach (var e in source)
- {
- snapshot.Add(e is null ? null : elementComparer.Snapshot(e.Value));
- }
-
- return (TCollection)(object)snapshot;
- }
-}
diff --git a/src/EFCore.Cosmos/ChangeTracking/Internal/NullableSingleDimensionalArrayComparer.cs b/src/EFCore.Cosmos/ChangeTracking/Internal/NullableSingleDimensionalArrayComparer.cs
deleted file mode 100644
index 240c16903d6..00000000000
--- a/src/EFCore.Cosmos/ChangeTracking/Internal/NullableSingleDimensionalArrayComparer.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace Microsoft.EntityFrameworkCore.Cosmos.ChangeTracking.Internal;
-
-///
-/// 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.
-///
-public sealed class NullableSingleDimensionalArrayComparer : ValueComparer
- where TElement : struct
-{
- ///
- /// 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.
- ///
- public NullableSingleDimensionalArrayComparer(ValueComparer elementComparer)
- : base(
- (a, b) => Compare(a, b, (ValueComparer)elementComparer),
- o => GetHashCode(o, (ValueComparer)elementComparer),
- source => Snapshot(source, (ValueComparer)elementComparer))
- {
- }
-
- ///
- /// 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.
- ///
- public override Type Type
- => typeof(TElement?[]);
-
- private static bool Compare(TElement?[]? a, TElement?[]? b, ValueComparer elementComparer)
- {
- if (a is null)
- {
- return b is null;
- }
-
- if (b is null || a.Length != b.Length)
- {
- return false;
- }
-
- if (ReferenceEquals(a, b))
- {
- return true;
- }
-
- for (var i = 0; i < a.Length; i++)
- {
- var (aElement, bElement) = (a[i], b[i]);
- if (aElement is null)
- {
- if (bElement is null)
- {
- continue;
- }
-
- return false;
- }
-
- if (bElement is null || !elementComparer.Equals(aElement, bElement))
- {
- return false;
- }
- }
-
- return true;
- }
-
- private static int GetHashCode(TElement?[] source, ValueComparer elementComparer)
- {
- var nullableEqualityComparer = new NullableEqualityComparer(elementComparer);
- var hash = new HashCode();
- foreach (var el in source)
- {
- hash.Add(el, nullableEqualityComparer);
- }
-
- return hash.ToHashCode();
- }
-
- private static TElement?[] Snapshot(TElement?[] source, ValueComparer elementComparer)
- {
- var snapshot = new TElement?[source.Length];
- for (var i = 0; i < source.Length; i++)
- {
- snapshot[i] = source[i] is { } value ? elementComparer.Snapshot(value) : null;
- }
-
- return snapshot;
- }
-}
diff --git a/src/EFCore.Cosmos/ChangeTracking/Internal/SingleDimensionalArrayComparer.cs b/src/EFCore.Cosmos/ChangeTracking/Internal/SingleDimensionalArrayComparer.cs
deleted file mode 100644
index f51422c4f73..00000000000
--- a/src/EFCore.Cosmos/ChangeTracking/Internal/SingleDimensionalArrayComparer.cs
+++ /dev/null
@@ -1,123 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using static System.Linq.Expressions.Expression;
-
-namespace Microsoft.EntityFrameworkCore.Cosmos.ChangeTracking.Internal;
-
-///
-/// 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.
-///
-public sealed class SingleDimensionalArrayComparer : ValueComparer
-{
- internal static readonly PropertyInfo ArrayLengthProperty
- = typeof(Array).GetRuntimeProperty(nameof(Array.Length))!;
-
- ///
- /// 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.
- ///
- public SingleDimensionalArrayComparer(ValueComparer elementComparer)
- : base(
- CreateEqualsExpression(elementComparer),
- CreateHashCodeExpression(elementComparer),
- CreateSnapshotExpression(elementComparer))
- {
- }
-
- ///
- /// 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.
- ///
- public override Type Type
- => typeof(TElement[]);
-
- private static Expression> CreateEqualsExpression(ValueComparer elementComparer)
- {
- var type = typeof(TElement[]);
- var param1 = Parameter(type, "v1");
- var param2 = Parameter(type, "v2");
-
- return Lambda>(
- Condition(
- Equal(param1, Constant(null, type)),
- Equal(param2, Constant(null, type)),
- AndAlso(
- NotEqual(param2, Constant(null, type)),
- AndAlso(
- Equal(MakeMemberAccess(param1, ArrayLengthProperty), MakeMemberAccess(param2, ArrayLengthProperty)),
- OrElse(
- ReferenceEqual(param1, param2),
- Call(
- EnumerableMethods.All.MakeGenericMethod(typeof(bool)),
- Call(
- EnumerableMethods.ZipWithSelector.MakeGenericMethod(typeof(TElement), typeof(TElement), typeof(bool)),
- param1,
- param2,
- elementComparer.EqualsExpression),
-#pragma warning disable EF1001 // Internal EF Core API usage.
- BoolIdentity))))),
-#pragma warning restore EF1001 // Internal EF Core API usage.
- param1, param2);
- }
-
- private static Expression> CreateHashCodeExpression(ValueComparer elementComparer)
- {
- var elementType = typeof(TElement);
- var param = Parameter(typeof(TElement[]), "v");
-
- var aggregateParam = Parameter(typeof(HashCode), "h");
- var aggregateElementParam = Parameter(elementType, "e");
-#pragma warning disable EF1001 // Internal EF Core API usage.
- var aggregateFunc = Lambda>(
- Call(HashCodeAddMethod, aggregateParam, elementComparer.ExtractHashCodeBody(aggregateElementParam)),
- aggregateParam, aggregateElementParam);
-
- var selector = Lambda>(
- Call(aggregateParam, ToHashCodeMethod),
- aggregateParam);
-#pragma warning restore EF1001 // Internal EF Core API usage.
-
- return Lambda>(
- Call(
- EnumerableMethods.AggregateWithSeedSelector.MakeGenericMethod(elementType, typeof(HashCode), typeof(int)),
- param,
- New(typeof(HashCode)),
- aggregateFunc,
- selector),
- param);
- }
-
- private static Expression> CreateSnapshotExpression(ValueComparer elementComparer)
- {
- var elementType = typeof(TElement);
- var param = Parameter(typeof(TElement[]), "v");
-
- var elementParam = Parameter(elementType, "e");
-
- var selector = elementType.IsValueType
- ? elementComparer.SnapshotExpression
- : Lambda>(
- Condition(
- Equal(elementParam, Constant(null, elementType)),
- Constant(null, elementType),
- elementComparer.ExtractSnapshotBody(elementParam)),
- elementParam);
-
- return Lambda>(
- Call(
- EnumerableMethods.ToArray.MakeGenericMethod(elementType),
- Call(
- EnumerableMethods.Select.MakeGenericMethod(elementType, elementType),
- param,
- selector)),
- param);
- }
-}
diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs
index 8734305b709..1fb048fd995 100644
--- a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs
+++ b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelValidator.cs
@@ -41,36 +41,6 @@ public override void Validate(IModel model, IDiagnosticsLogger
- protected override void ValidatePrimitiveCollections(
- IModel model,
- IDiagnosticsLogger logger)
- {
- foreach (var entityType in model.GetEntityTypes())
- {
- Validate(entityType, logger);
- }
-
- static void Validate(ITypeBase typeBase, IDiagnosticsLogger logger)
- {
- foreach (var property in typeBase.GetDeclaredProperties())
- {
- if (property is { IsPrimitiveCollection: true })
- {
- throw new InvalidOperationException(
- CosmosStrings.PrimitiveCollectionsNotSupported(
- property.DeclaringType.ClrType.ShortDisplayName(),
- property.Name));
- }
- }
-
- foreach (var complexProperty in typeBase.GetDeclaredComplexProperties())
- {
- Validate(complexProperty.ComplexType, logger);
- }
- }
- }
-
///
/// 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
diff --git a/src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs b/src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs
index e7561a9f10a..aecd79717d0 100644
--- a/src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs
+++ b/src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs
@@ -309,14 +309,6 @@ public static string PartitionKeyStoreNameMismatch(object? property1, object? en
GetString("PartitionKeyStoreNameMismatch", nameof(property1), nameof(entityType1), nameof(storeName1), nameof(property2), nameof(entityType2), nameof(storeName2)),
property1, entityType1, storeName1, property2, entityType2, storeName2);
- ///
- /// The property '{entityType}.{property}' is configured as an EF8 primitive collection. Primitive collections in a Cosmos model are discovered by convention.
- ///
- public static string PrimitiveCollectionsNotSupported(object? entityType, object? property)
- => string.Format(
- GetString("PrimitiveCollectionsNotSupported", nameof(entityType), nameof(property)),
- entityType, property);
-
///
/// Unable to execute a 'ReadItem' query since the 'id' value is missing and cannot be generated.
///
diff --git a/src/EFCore.Cosmos/Properties/CosmosStrings.resx b/src/EFCore.Cosmos/Properties/CosmosStrings.resx
index e5815bbdd9b..0709e132b3a 100644
--- a/src/EFCore.Cosmos/Properties/CosmosStrings.resx
+++ b/src/EFCore.Cosmos/Properties/CosmosStrings.resx
@@ -263,9 +263,6 @@
The partition key property '{property1}' on '{entityType1}' is mapped as '{storeName1}', but the partition key property '{property2}' on '{entityType2}' is mapped as '{storeName2}'. All partition key properties need to be mapped to the same store property for entity types mapped to the same container.
-
- The property '{entityType}.{property}' is configured as an EF8 primitive collection. Primitive collections in a Cosmos model are discovered by convention.
-
Unable to execute a 'ReadItem' query since the 'id' value is missing and cannot be generated.
diff --git a/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMappingSource.cs b/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMappingSource.cs
index 8f7cc37bd56..b24d1a1fcab 100644
--- a/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMappingSource.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMappingSource.cs
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Collections.ObjectModel;
using Microsoft.EntityFrameworkCore.Cosmos.ChangeTracking.Internal;
using Newtonsoft.Json.Linq;
@@ -86,35 +85,9 @@ public CosmosTypeMappingSource(TypeMappingSourceDependencies dependencies)
var jsonValueReaderWriter = Dependencies.JsonValueReaderWriterSource.FindReaderWriter(clrType);
- if (clrType.IsArray)
- {
- var elementMappingInfo = new TypeMappingInfo(elementType);
- var elementMapping = FindPrimitiveMapping(elementMappingInfo)
- ?? FindCollectionMapping(elementMappingInfo);
- return elementMapping == null
- ? null
- : new CosmosTypeMapping(
- clrType, CreateArrayComparer(elementMapping, elementType), jsonValueReaderWriter: jsonValueReaderWriter);
- }
-
if (clrType is { IsGenericType: true, IsGenericTypeDefinition: false })
{
var genericTypeDefinition = clrType.GetGenericTypeDefinition();
- if (genericTypeDefinition == typeof(List<>)
- || genericTypeDefinition == typeof(IList<>)
- || genericTypeDefinition == typeof(IReadOnlyList<>)
- || genericTypeDefinition == typeof(ObservableCollection<>)
- || genericTypeDefinition == typeof(Collection<>))
- {
- var elementMappingInfo = new TypeMappingInfo(elementType);
- var elementMapping = FindPrimitiveMapping(elementMappingInfo)
- ?? FindCollectionMapping(elementMappingInfo);
- return elementMapping == null
- ? null
- : new CosmosTypeMapping(
- clrType, CreateListComparer(elementMapping, elementType, clrType), jsonValueReaderWriter: jsonValueReaderWriter);
- }
-
if (genericTypeDefinition == typeof(Dictionary<,>)
|| genericTypeDefinition == typeof(IDictionary<,>)
|| genericTypeDefinition == typeof(IReadOnlyDictionary<,>))
@@ -140,33 +113,6 @@ public CosmosTypeMappingSource(TypeMappingSourceDependencies dependencies)
return null;
}
- private static ValueComparer CreateArrayComparer(CoreTypeMapping elementMapping, Type elementType)
- {
- var unwrappedType = elementType.UnwrapNullableType();
-
- return (ValueComparer)Activator.CreateInstance(
- elementType == unwrappedType
- ? typeof(SingleDimensionalArrayComparer<>).MakeGenericType(elementType)
- : typeof(NullableSingleDimensionalArrayComparer<>).MakeGenericType(unwrappedType),
- elementMapping.Comparer)!;
- }
-
- private static ValueComparer CreateListComparer(
- CoreTypeMapping elementMapping,
- Type elementType,
- Type listType,
- bool readOnly = false)
- {
- var unwrappedType = elementType.UnwrapNullableType();
-
- return (ValueComparer)Activator.CreateInstance(
- elementType == unwrappedType
- ? typeof(ListComparer<,>).MakeGenericType(elementType, listType)
- : typeof(NullableListComparer<,>).MakeGenericType(unwrappedType, listType),
- elementMapping.Comparer,
- readOnly)!;
- }
-
private static ValueComparer CreateStringDictionaryComparer(
CoreTypeMapping elementMapping,
Type elementType,
diff --git a/src/EFCore.Relational/Storage/RelationalTypeMappingSource.cs b/src/EFCore.Relational/Storage/RelationalTypeMappingSource.cs
index 3a685cf279e..224eaa7e9ae 100644
--- a/src/EFCore.Relational/Storage/RelationalTypeMappingSource.cs
+++ b/src/EFCore.Relational/Storage/RelationalTypeMappingSource.cs
@@ -144,60 +144,64 @@ protected override CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo)
var (mappingInfo, providerClrType, customConverter, elementMapping) = k;
var sourceType = mappingInfo.ClrType;
- var mapping = providerClrType == null
- || providerClrType == mappingInfo.ClrType
- ? self.FindMapping(mappingInfo)
- : null;
+ RelationalTypeMapping? mapping = null;
- if (mapping == null)
+ if (providerClrType == null
+ || providerClrType == mappingInfo.ClrType)
{
- if (elementMapping == null
- || customConverter != null)
+ mapping = self.FindMapping(mappingInfo);
+ }
+
+ if (mapping == null
+ && sourceType != null)
+ {
+ if (elementMapping == null)
+ {
+ mapping = WithConverter();
+ }
+
+ mapping ??= self.FindCollectionMapping(mappingInfo, sourceType, providerClrType, elementMapping)
+ ?? WithConverter();
+ }
+
+ RelationalTypeMapping? WithConverter()
+ {
+ foreach (var converterInfo in self.Dependencies
+ .ValueConverterSelector
+ .Select(sourceType, providerClrType))
{
- if (sourceType != null)
+ var mappingInfoUsed = mappingInfo.WithConverter(converterInfo);
+ mapping = self.FindMapping(mappingInfoUsed);
+
+ if (mapping == null
+ && providerClrType != null)
{
- foreach (var converterInfo in self.Dependencies
+ foreach (var secondConverterInfo in self.Dependencies
.ValueConverterSelector
- .Select(sourceType, providerClrType))
+ .Select(providerClrType))
{
- var mappingInfoUsed = mappingInfo.WithConverter(converterInfo);
- mapping = self.FindMapping(mappingInfoUsed);
-
- if (mapping == null
- && providerClrType != null)
- {
- foreach (var secondConverterInfo in self.Dependencies
- .ValueConverterSelector
- .Select(providerClrType))
- {
- mapping = self.FindMapping(mappingInfoUsed.WithConverter(secondConverterInfo));
-
- if (mapping != null)
- {
- mapping = (RelationalTypeMapping)mapping.WithComposedConverter(
- secondConverterInfo.Create(),
- jsonValueReaderWriter: mappingInfoUsed.JsonValueReaderWriter);
- break;
- }
- }
- }
+ mapping = self.FindMapping(mappingInfoUsed.WithConverter(secondConverterInfo));
if (mapping != null)
{
mapping = (RelationalTypeMapping)mapping.WithComposedConverter(
- converterInfo.Create(),
- jsonValueReaderWriter: mappingInfo.JsonValueReaderWriter);
+ secondConverterInfo.Create(),
+ jsonValueReaderWriter: mappingInfoUsed.JsonValueReaderWriter);
break;
}
}
+ }
- mapping ??= self.FindCollectionMapping(mappingInfo, sourceType, providerClrType, elementMapping);
+ if (mapping != null)
+ {
+ mapping = (RelationalTypeMapping)mapping.WithComposedConverter(
+ converterInfo.Create(),
+ jsonValueReaderWriter: mappingInfo.JsonValueReaderWriter);
+ break;
}
}
- else if (sourceType != null)
- {
- mapping = self.FindCollectionMapping(mappingInfo, sourceType, providerClrType, elementMapping);
- }
+
+ return mapping;
}
if (mapping != null
diff --git a/src/EFCore/ChangeTracking/NullableValueTypeListComparer.cs b/src/EFCore/ChangeTracking/ListOfNullableValueTypesComparer.cs
similarity index 80%
rename from src/EFCore/ChangeTracking/NullableValueTypeListComparer.cs
rename to src/EFCore/ChangeTracking/ListOfNullableValueTypesComparer.cs
index 7acbff280f9..ba34d114ee6 100644
--- a/src/EFCore/ChangeTracking/NullableValueTypeListComparer.cs
+++ b/src/EFCore/ChangeTracking/ListOfNullableValueTypesComparer.cs
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Collections.ObjectModel;
+
namespace Microsoft.EntityFrameworkCore.ChangeTracking;
///
@@ -9,22 +11,29 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking;
///
///
///
-/// This comparer should be used for nullable value types. Use for reference
+/// This comparer should be used for nullable value types. Use for reference
/// types and non-nullable value types.
///
///
/// See EF Core value comparers for more information and examples.
///
///
+/// The collection type to create an index of, if needed.
/// The element type.
-public sealed class NullableValueTypeListComparer : ValueComparer>
+public sealed class ListOfNullableValueTypesComparer : ValueComparer>
where TElement : struct
{
+ private static readonly bool IsArray = typeof(TConcreteCollection).IsArray;
+
+ private static readonly bool IsReadOnly = IsArray
+ || (typeof(TConcreteCollection).IsGenericType
+ && typeof(TConcreteCollection).GetGenericTypeDefinition() == typeof(ReadOnlyCollection<>));
+
///
/// Creates a new instance of the list comparer.
///
/// The comparer to use for comparing elements.
- public NullableValueTypeListComparer(ValueComparer elementComparer)
+ public ListOfNullableValueTypesComparer(ValueComparer elementComparer)
: base(
(a, b) => Compare(a, b, (ValueComparer)elementComparer),
o => GetHashCode(o, (ValueComparer)elementComparer),
@@ -117,10 +126,9 @@ private static int GetHashCode(IEnumerable source, ValueComparer).MakeGenericType(elementComparer.Type.MakeNullable()).ShortDisplayName()));
}
- if (sourceList.IsReadOnly)
+ if (IsArray)
{
var snapshot = new TElement?[sourceList.Count];
-
for (var i = 0; i < sourceList.Count; i++)
{
var instance = sourceList[i];
@@ -131,16 +139,15 @@ private static int GetHashCode(IEnumerable source, ValueComparer || sourceList.IsReadOnly
- ? new List(sourceList.Count)
- : (IList)Activator.CreateInstance(source.GetType())!;
-
+ var snapshot = IsReadOnly ? new List() : (IList)Activator.CreateInstance()!;
foreach (var e in sourceList)
{
snapshot.Add(e == null ? null : elementComparer.Snapshot(e));
}
- return snapshot;
+ return IsReadOnly
+ ? (IList)Activator.CreateInstance(typeof(TConcreteCollection), [snapshot])!
+ : snapshot;
}
}
}
diff --git a/src/EFCore/ChangeTracking/ObjectListComparer.cs b/src/EFCore/ChangeTracking/ListOfReferenceTypesComparer.cs
similarity index 66%
rename from src/EFCore/ChangeTracking/ObjectListComparer.cs
rename to src/EFCore/ChangeTracking/ListOfReferenceTypesComparer.cs
index 9024b6d97e4..0523dc10006 100644
--- a/src/EFCore/ChangeTracking/ObjectListComparer.cs
+++ b/src/EFCore/ChangeTracking/ListOfReferenceTypesComparer.cs
@@ -1,6 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Collections;
+using System.Collections.ObjectModel;
+
namespace Microsoft.EntityFrameworkCore.ChangeTracking;
///
@@ -15,17 +18,25 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking;
/// See EF Core value comparers for more information and examples.
///
///
+/// The collection type to create an index of, if needed.
/// The element type.
-public sealed class ObjectListComparer : ValueComparer>
+public sealed class ListOfReferenceTypesComparer : ValueComparer
public ValueComparer ElementComparer { get; }
- private static bool Compare(IEnumerable? a, IEnumerable? b, ValueComparer elementComparer)
+ private static bool Compare(object? a, object? b, ValueComparer elementComparer)
{
if (ReferenceEquals(a, b))
{
@@ -53,7 +64,7 @@ private static bool Compare(IEnumerable? a, IEnumerable? b,
return false;
}
- if (a is IList