diff --git a/src/EFCore.PG/Storage/Internal/Mapping/NpgsqlListTypeMapping.cs b/src/EFCore.PG/Storage/Internal/Mapping/NpgsqlListTypeMapping.cs
index 1f024448f..49d6741c8 100644
--- a/src/EFCore.PG/Storage/Internal/Mapping/NpgsqlListTypeMapping.cs
+++ b/src/EFCore.PG/Storage/Internal/Mapping/NpgsqlListTypeMapping.cs
@@ -1,4 +1,5 @@
#region License
+
// The PostgreSQL License
//
// Copyright (C) 2016 The Npgsql Development Team
@@ -19,9 +20,11 @@
// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS
// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
#endregion
using System;
+using System.Collections;
using System.Text;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
@@ -32,53 +35,60 @@
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping
{
///
- /// Maps PostgreSQL arrays to .NET List{T}.
+ /// Maps PostgreSQL arrays to .
///
public class NpgsqlListTypeMapping : RelationalTypeMapping
{
+ ///
+ /// The CLR type of the list items.
+ ///
public RelationalTypeMapping ElementMapping { get; }
///
- /// Creates the default array mapping (i.e. for the single-dimensional CLR array type)
+ /// Creates the default list mapping.
///
public NpgsqlListTypeMapping(RelationalTypeMapping elementMapping, Type listType)
- : this(elementMapping.StoreType + "[]", elementMapping, listType)
- {}
+ : this(elementMapping.StoreType + "[]", elementMapping, listType) {}
+ ///
NpgsqlListTypeMapping(string storeType, RelationalTypeMapping elementMapping, Type listType)
- : base(new RelationalTypeMappingParameters(
- new CoreTypeMappingParameters(listType, null, CreateComparer(elementMapping, listType)), storeType
- ))
- {
- ElementMapping = elementMapping;
- }
+ : base(
+ new RelationalTypeMappingParameters(
+ new CoreTypeMappingParameters(listType, null, CreateComparer(elementMapping, listType)), storeType))
+ => ElementMapping = elementMapping;
+ ///
protected NpgsqlListTypeMapping(RelationalTypeMappingParameters parameters, RelationalTypeMapping elementMapping)
- : base(parameters) {}
+ : base(parameters)
+ => ElementMapping = elementMapping;
+ ///
public override RelationalTypeMapping Clone(string storeType, int? size)
=> new NpgsqlListTypeMapping(StoreType, ElementMapping, ClrType);
+ ///
public override CoreTypeMapping Clone(ValueConverter converter)
=> new NpgsqlListTypeMapping(Parameters.WithComposedConverter(converter), ElementMapping);
+ ///
protected override string GenerateNonNullSqlLiteral(object value)
{
- // TODO: Duplicated from NpgsqlArrayTypeMapping
- var arr = (Array)value;
+ var list = (IList)value;
- if (arr.Rank != 1)
+ if (list.GetType().GenericTypeArguments[0] != ElementMapping.ClrType)
throw new NotSupportedException("Multidimensional array literals aren't supported");
var sb = new StringBuilder();
sb.Append("ARRAY[");
- for (var i = 0; i < arr.Length; i++)
+ for (var i = 0; i < list.Count; i++)
{
- sb.Append(ElementMapping.GenerateSqlLiteral(arr.GetValue(i)));
- if (i < arr.Length - 1)
- sb.Append(",");
+ if (i > 0)
+ sb.Append(',');
+
+ sb.Append(ElementMapping.GenerateSqlLiteral(list[i]));
}
- sb.Append("]");
+
+ sb.Append(']');
return sb.ToString();
}
@@ -91,14 +101,13 @@ protected override string GenerateNonNullSqlLiteral(object value)
static ValueComparer CreateComparer(RelationalTypeMapping elementMapping, Type listType)
{
Debug.Assert(listType.IsGenericType && listType.GetGenericTypeDefinition() == typeof(List<>));
+
var elementType = listType.GetGenericArguments()[0];
// We use different comparer implementations based on whether we have a non-null element comparer,
// and if not, whether the element is IEquatable
-
if (elementMapping.Comparer != null)
- return (ValueComparer)Activator.CreateInstance(
- typeof(SingleDimComparerWithComparer<>).MakeGenericType(elementType), elementMapping);
+ return (ValueComparer)Activator.CreateInstance(typeof(SingleDimComparerWithComparer<>).MakeGenericType(elementType), elementMapping);
if (typeof(IEquatable<>).MakeGenericType(elementType).IsAssignableFrom(elementType))
return (ValueComparer)Activator.CreateInstance(typeof(SingleDimComparerWithIEquatable<>).MakeGenericType(elementType));
@@ -110,10 +119,11 @@ static ValueComparer CreateComparer(RelationalTypeMapping elementMapping, Type l
class SingleDimComparerWithComparer : ValueComparer>
{
- public SingleDimComparerWithComparer(RelationalTypeMapping elementMapping) : base(
- (a, b) => Compare(a, b, (ValueComparer)elementMapping.Comparer),
- o => o.GetHashCode(), // TODO: Need to get hash code of elements...
- source => Snapshot(source, (ValueComparer)elementMapping.Comparer)) {}
+ public SingleDimComparerWithComparer(RelationalTypeMapping elementMapping)
+ : base(
+ (a, b) => Compare(a, b, (ValueComparer)elementMapping.Comparer),
+ o => o.GetHashCode(), // TODO: Need to get hash code of elements...
+ source => Snapshot(source, (ValueComparer)elementMapping.Comparer)) {}
public override Type Type => typeof(List);
@@ -133,25 +143,27 @@ static bool Compare(List a, List b, ValueComparer elementCo
static List Snapshot(List source, ValueComparer elementComparer)
{
- if (source == null)
+ if (source is null)
return null;
var snapshot = new List(source.Count);
+
// Note: the following currently boxes every element access because ValueComparer isn't really
// generic (see https://github.com/aspnet/EntityFrameworkCore/issues/11072)
foreach (var e in source)
snapshot.Add(elementComparer.Snapshot(e));
+
return snapshot;
}
}
- class SingleDimComparerWithIEquatable : ValueComparer>
- where TElem : IEquatable
+ class SingleDimComparerWithIEquatable : ValueComparer> where TElem : IEquatable
{
- public SingleDimComparerWithIEquatable(): base(
- (a, b) => Compare(a, b),
- o => o.GetHashCode(), // TODO: Need to get hash code of elements...
- source => DoSnapshot(source)) {}
+ public SingleDimComparerWithIEquatable()
+ : base(
+ (a, b) => Compare(a, b),
+ o => o.GetHashCode(), // TODO: Need to get hash code of elements...
+ source => DoSnapshot(source)) {}
public override Type Type => typeof(List);
@@ -169,8 +181,10 @@ static bool Compare(List a, List b)
{
if (elem2 == null)
continue;
+
return false;
}
+
if (!elem1.Equals(elem2))
return false;
}
@@ -180,21 +194,25 @@ static bool Compare(List a, List b)
static List DoSnapshot(List source)
{
- if (source == null)
+ if (source is null)
return null;
+
var snapshot = new List(source.Count);
+
foreach (var e in source)
snapshot.Add(e);
+
return snapshot;
}
}
class SingleDimComparerWithEquals : ValueComparer>
{
- public SingleDimComparerWithEquals() : base(
- (a, b) => Compare(a, b),
- o => o.GetHashCode(), // TODO: Need to get hash code of elements...
- source => DoSnapshot(source)) {}
+ public SingleDimComparerWithEquals()
+ : base(
+ (a, b) => Compare(a, b),
+ o => o.GetHashCode(), // TODO: Need to get hash code of elements...
+ source => DoSnapshot(source)) {}
public override Type Type => typeof(List);
@@ -215,6 +233,7 @@ static bool Compare(List a, List b)
continue;
return false;
}
+
if (!elem1.Equals(elem2))
return false;
}
@@ -224,14 +243,16 @@ static bool Compare(List a, List b)
static List DoSnapshot(List source)
{
- if (source == null)
+ if (source is null)
return null;
var snapshot = new List(source.Count);
+
// Note: the following currently boxes every element access because ValueComparer isn't really
// generic (see https://github.com/aspnet/EntityFrameworkCore/issues/11072)
foreach (var e in source)
snapshot.Add(e);
+
return snapshot;
}
}