diff --git a/src/EFCore.PG.NodaTime/Storage/Internal/NpgsqlNodaTimeTypeMappingSourcePlugin.cs b/src/EFCore.PG.NodaTime/Storage/Internal/NpgsqlNodaTimeTypeMappingSourcePlugin.cs
index b6d135ce65..1999e9ab0a 100644
--- a/src/EFCore.PG.NodaTime/Storage/Internal/NpgsqlNodaTimeTypeMappingSourcePlugin.cs
+++ b/src/EFCore.PG.NodaTime/Storage/Internal/NpgsqlNodaTimeTypeMappingSourcePlugin.cs
@@ -155,9 +155,20 @@ RelationalTypeMapping FindArrayMapping(in RelationalTypeMappingInfo mappingInfo)
var elementStoreType = storeType.Substring(0, storeType.Length - 2);
var elementMapping = FindExistingMapping(new RelationalTypeMappingInfo(elementStoreType, elementStoreType,
mappingInfo.IsUnicode, mappingInfo.Size, mappingInfo.Precision, mappingInfo.Scale));
+
if (elementMapping != null)
- return StoreTypeMappings.GetOrAdd(storeType,
- new RelationalTypeMapping[] { new NpgsqlArrayTypeMapping(storeType, elementMapping) })[0];
+ {
+ var added = StoreTypeMappings.TryAdd(storeType,
+ new RelationalTypeMapping[]
+ {
+ new NpgsqlArrayArrayTypeMapping(storeType, elementMapping),
+ new NpgsqlArrayListTypeMapping(storeType, elementMapping)
+ });
+ Debug.Assert(added);
+ var mapping = FindExistingMapping(mappingInfo);
+ Debug.Assert(mapping != null);
+ return mapping;
+ }
}
var clrType = mappingInfo.ClrType;
@@ -178,7 +189,7 @@ RelationalTypeMapping FindArrayMapping(in RelationalTypeMappingInfo mappingInfo)
if (elementMapping is NpgsqlArrayTypeMapping)
return null;
- return ClrTypeMappings.GetOrAdd(clrType, new NpgsqlArrayTypeMapping(elementMapping, clrType));
+ return ClrTypeMappings.GetOrAdd(clrType, new NpgsqlArrayArrayTypeMapping(elementMapping, clrType));
}
if (clrType.IsGenericType && clrType.GetGenericTypeDefinition() == typeof(List<>))
@@ -194,7 +205,7 @@ RelationalTypeMapping FindArrayMapping(in RelationalTypeMappingInfo mappingInfo)
if (elementMapping is NpgsqlArrayTypeMapping)
return null;
- return ClrTypeMappings.GetOrAdd(clrType, new NpgsqlListTypeMapping(elementMapping, clrType));
+ return ClrTypeMappings.GetOrAdd(clrType, new NpgsqlArrayListTypeMapping(elementMapping, clrType));
}
return null;
diff --git a/src/EFCore.PG/Extensions/TypeExtensions.cs b/src/EFCore.PG/Extensions/TypeExtensions.cs
new file mode 100644
index 0000000000..84cf891375
--- /dev/null
+++ b/src/EFCore.PG/Extensions/TypeExtensions.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+
+// ReSharper disable once CheckNamespace
+namespace Npgsql.EntityFrameworkCore.PostgreSQL
+{
+ internal static class TypeExtensions
+ {
+ internal static bool IsGenericList(this Type type)
+ => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>);
+
+ internal static bool IsArrayOrGenericList(this Type type)
+ => type.IsArray || type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>);
+ }
+}
diff --git a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlArrayMethodTranslator.cs b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlArrayTranslator.cs
similarity index 74%
rename from src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlArrayMethodTranslator.cs
rename to src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlArrayTranslator.cs
index edab868c2e..1971673c45 100644
--- a/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlArrayMethodTranslator.cs
+++ b/src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlArrayTranslator.cs
@@ -13,12 +13,12 @@
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal
{
///
- /// Translates functions on arrays into their corresponding PostgreSQL operations.
+ /// Translates method and property calls on arrays/lists into their corresponding PostgreSQL operations.
///
///
/// https://www.postgresql.org/docs/current/static/functions-array.html
///
- public class NpgsqlArrayMethodTranslator : IMethodCallTranslator
+ public class NpgsqlArrayTranslator : IMethodCallTranslator, IMemberTranslator
{
[NotNull] static readonly MethodInfo SequenceEqual =
typeof(Enumerable).GetTypeInfo().GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly)
@@ -38,7 +38,7 @@ public class NpgsqlArrayMethodTranslator : IMethodCallTranslator
[NotNull]
readonly NpgsqlJsonPocoTranslator _jsonPocoTranslator;
- public NpgsqlArrayMethodTranslator(NpgsqlSqlExpressionFactory sqlExpressionFactory, NpgsqlJsonPocoTranslator jsonPocoTranslator)
+ public NpgsqlArrayTranslator(NpgsqlSqlExpressionFactory sqlExpressionFactory, NpgsqlJsonPocoTranslator jsonPocoTranslator)
{
_sqlExpressionFactory = sqlExpressionFactory;
_jsonPocoTranslator = jsonPocoTranslator;
@@ -47,7 +47,17 @@ public NpgsqlArrayMethodTranslator(NpgsqlSqlExpressionFactory sqlExpressionFacto
[CanBeNull]
public SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList arguments)
{
- // TODO: Fully support List<>
+ if (instance != null && instance.Type.IsGenericList())
+ {
+ if (method.Name == "get_Item" && arguments.Count == 1)
+ {
+ return
+ // Try translating indexing inside json column
+ _jsonPocoTranslator.TranslateMemberAccess(instance, arguments[0], method.ReturnType) ??
+ // Other types should be subscriptable - but PostgreSQL arrays are 1-based, so adjust the index.
+ _sqlExpressionFactory.ArrayIndex(instance, GenerateOneBasedIndexExpression(arguments[0]));
+ }
+ }
if (arguments.Count == 0)
return null;
@@ -56,7 +66,7 @@ public SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadO
var operandElementType = operand.Type.IsArray
? operand.Type.GetElementType()
- : operand.Type.IsGenericType && operand.Type.GetGenericTypeDefinition() == typeof(List<>)
+ : operand.Type.IsGenericList()
? operand.Type.GetGenericArguments()[0]
: null;
@@ -122,5 +132,25 @@ public SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadO
return null;
}
+
+ public SqlExpression Translate(SqlExpression instance, MemberInfo member, Type returnType)
+ {
+ if (instance != null && instance.Type.IsGenericList() && member.Name == nameof(List