提交 9d17b945 编写于 作者: S Shay Rojansky

Add support for arrays

Does not include scaffolding

Work on #15
上级 b7b56e6b
......@@ -81,6 +81,8 @@
<Compile Include="Query\ExpressionTranslators\Internal\NpgsqlCompositeMemberTranslator.cs" />
<Compile Include="Query\ExpressionTranslators\Internal\NpgsqlCompositeMethodCallTranslator.cs" />
<Compile Include="Metadata\Conventions\Internal\NpgsqlConventionSetBuilder.cs" />
<Compile Include="Storage\Internal\Mapping\NpgsqlArrayTypeMapping.cs" />
<Compile Include="Storage\Internal\Mapping\NpgsqlTypeMapping.cs" />
<Compile Include="Storage\Internal\NpgsqlSqlGenerationHelper.cs" />
<Compile Include="Storage\Internal\NpgsqlRelationalConnection.cs" />
<Compile Include="Storage\Internal\NpgsqlDatabaseCreator.cs" />
......@@ -89,7 +91,7 @@
<Compile Include="Infrastructure\Internal\NpgsqlModelSource.cs" />
<Compile Include="Infrastructure\Internal\NpgsqlOptionsExtension.cs" />
<Compile Include="Storage\Internal\NpgsqlTypeMapper.cs" />
<Compile Include="Storage\Internal\NpgsqlTypeMapping.cs" />
<Compile Include="Storage\Internal\Mapping\NpgsqlBaseTypeMapping.cs" />
<Compile Include="Update\Internal\NpgsqlUpdateSqlGenerator.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="..\Shared\CommonAssemblyInfo.cs">
......
......@@ -32,26 +32,17 @@
namespace Microsoft.EntityFrameworkCore.Storage.Internal
{
public class NpgsqlTypeMapping : RelationalTypeMapping
public sealed class NpgsqlArrayTypeMapping : NpgsqlTypeMapping
{
public new NpgsqlDbType? StoreType { get; }
public NpgsqlTypeMapping ElementMapping { get; private set; }
internal NpgsqlTypeMapping([NotNull] string defaultTypeName, [NotNull] Type clrType, NpgsqlDbType storeType)
: base(defaultTypeName, clrType)
internal NpgsqlArrayTypeMapping(Type arrayClrType, NpgsqlTypeMapping elementMapping)
: base('_' + elementMapping.StoreType, arrayClrType)
{
StoreType = storeType;
}
internal NpgsqlTypeMapping([NotNull] string defaultTypeName, [NotNull] Type clrType)
: base(defaultTypeName, clrType)
{ }
ElementMapping = elementMapping;
protected override void ConfigureParameter([NotNull] DbParameter parameter)
{
if (StoreType.HasValue)
{
((NpgsqlParameter)parameter).NpgsqlDbType = StoreType.Value;
}
if (elementMapping.NpgsqlDbType.HasValue)
NpgsqlDbType = elementMapping.NpgsqlDbType.Value | NpgsqlTypes.NpgsqlDbType.Array;
}
}
}
#region License
// The PostgreSQL License
//
// Copyright (C) 2016 The Npgsql Development Team
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph and the following two paragraphs appear in all copies.
//
// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY
// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//
// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// 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.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using JetBrains.Annotations;
using Npgsql;
using NpgsqlTypes;
namespace Microsoft.EntityFrameworkCore.Storage.Internal
{
public sealed class NpgsqlBaseTypeMapping : NpgsqlTypeMapping
{
internal NpgsqlBaseTypeMapping([NotNull] string storeType, [NotNull] Type clrType, NpgsqlDbType npgsqlDbType)
: base(storeType, clrType, npgsqlDbType) { }
internal NpgsqlBaseTypeMapping([NotNull] string storeType, [NotNull] Type clrType)
: base(storeType, clrType) {}
}
}
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Storage;
using Npgsql;
using NpgsqlTypes;
namespace Microsoft.EntityFrameworkCore.Storage.Internal
{
public class NpgsqlTypeMapping : RelationalTypeMapping
{
internal NpgsqlTypeMapping([NotNull] string storeType, [NotNull] Type clrType, NpgsqlDbType npgsqlDbType)
: base(storeType, clrType)
{
NpgsqlDbType = npgsqlDbType;
}
internal NpgsqlTypeMapping([NotNull] string storeType, [NotNull] Type clrType)
: base(storeType, clrType) {}
public NpgsqlDbType? NpgsqlDbType { get; protected set; }
protected override void ConfigureParameter([NotNull] DbParameter parameter)
{
if (NpgsqlDbType.HasValue)
((NpgsqlParameter)parameter).NpgsqlDbType = NpgsqlDbType.Value;
}
}
}
......@@ -22,6 +22,7 @@
#endregion
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
......@@ -32,16 +33,20 @@
using Microsoft.EntityFrameworkCore.Utilities;
using Npgsql;
using Npgsql.TypeHandlers;
using NpgsqlTypes;
namespace Microsoft.EntityFrameworkCore.Storage.Internal
{
public class NpgsqlTypeMapper : RelationalTypeMapper
{
readonly Dictionary<string, RelationalTypeMapping> _storeTypeMappings;
readonly Dictionary<Type, RelationalTypeMapping> _clrTypeMappings;
readonly ConcurrentDictionary<int, RelationalTypeMapping> _boundedStringMappings
= new ConcurrentDictionary<int, RelationalTypeMapping>();
readonly Dictionary<string, RelationalTypeMapping> _storeTypeMappings;
readonly Dictionary<Type, RelationalTypeMapping> _clrTypeMappings;
readonly ConcurrentDictionary<Type, NpgsqlArrayTypeMapping> _arrayMappings
= new ConcurrentDictionary<Type, NpgsqlArrayTypeMapping>();
public NpgsqlTypeMapper()
{
......@@ -51,7 +56,7 @@ public NpgsqlTypeMapper()
.Where(tam => tam.Mapping.NpgsqlDbType.HasValue)
.Select(tam => new {
Name = tam.Mapping.PgName,
Mapping = (RelationalTypeMapping)new NpgsqlTypeMapping(tam.Mapping.PgName, GetTypeHandlerTypeArgument(tam.HandlerType), tam.Mapping.NpgsqlDbType.Value)
Mapping = (RelationalTypeMapping)new NpgsqlBaseTypeMapping(tam.Mapping.PgName, GetTypeHandlerTypeArgument(tam.HandlerType), tam.Mapping.NpgsqlDbType.Value)
})
// Enums
//.Concat(TypeHandlerRegistry.GlobalEnumMappings.Select(kv => new {
......@@ -73,7 +78,7 @@ public NpgsqlTypeMapper()
.Where(m => m.NpgsqlDbType.HasValue)
.SelectMany(m => m.ClrTypes, (m, t) => new {
Type = t,
Mapping = (RelationalTypeMapping)new NpgsqlTypeMapping(m.PgName, t, m.NpgsqlDbType.Value)
Mapping = (RelationalTypeMapping)new NpgsqlBaseTypeMapping(m.PgName, t, m.NpgsqlDbType.Value)
})
// Enums
//.Concat(TypeHandlerRegistry.GlobalEnumMappings.Select(kv => new {
......@@ -97,18 +102,48 @@ public NpgsqlTypeMapper()
protected override IReadOnlyDictionary<string, RelationalTypeMapping> GetStoreTypeMappings()
=> _storeTypeMappings;
[CanBeNull]
public override RelationalTypeMapping FindMapping(Type clrType)
{
var mapping = base.FindMapping(clrType);
if (mapping != null)
return mapping;
// Check if it's an array or generic IList
Type arrayElementType = null;
if (clrType.IsArray)
arrayElementType = clrType.GetElementType();
else if (typeof(IList).IsAssignableFrom(clrType) && clrType.GetTypeInfo().IsGenericType)
arrayElementType = clrType.GetGenericArguments()[0];
if (arrayElementType != null)
{
var elementMapping = (NpgsqlBaseTypeMapping)FindMapping(arrayElementType);
// If an element isn't supported, neither is its array
if (elementMapping?.NpgsqlDbType == null)
return null;
return _arrayMappings.GetOrAdd(clrType, t => new NpgsqlArrayTypeMapping(clrType, elementMapping));
}
return null;
}
[CanBeNull]
protected override RelationalTypeMapping FindCustomMapping(IProperty property)
{
Check.NotNull(property, nameof(property));
if (property.ClrType == typeof(string))
var clrType = property.ClrType;
if (clrType == typeof(string))
{
var maxLength = property.GetMaxLength();
if (maxLength.HasValue)
{
return _boundedStringMappings.GetOrAdd(maxLength.Value,
ml => new NpgsqlTypeMapping($"varchar({maxLength})", typeof(string))
ml => new NpgsqlBaseTypeMapping($"varchar({maxLength})", typeof(string))
);
}
}
......
......@@ -119,6 +119,12 @@ public override void OnModelCreating(ModelBuilder modelBuilder)
// Jsonb in .NET is a regular string
modelBuilder.Entity<MappedDataTypes>().Property(e => e.Jsonb).HasColumnType("jsonb");
modelBuilder.Entity<MappedNullableDataTypes>().Property(e => e.Jsonb).HasColumnType("jsonb");
// Arrays
modelBuilder.Entity<MappedDataTypes>().Property(e => e.PrimitiveArray).HasColumnType("_int4");
modelBuilder.Entity<MappedNullableDataTypes>().Property(e => e.PrimitiveArray).HasColumnType("_int4");
modelBuilder.Entity<MappedDataTypes>().Property(e => e.NonPrimitiveArray).HasColumnType("_macaddr");
modelBuilder.Entity<MappedNullableDataTypes>().Property(e => e.NonPrimitiveArray).HasColumnType("_macaddr");
}
private static void MapColumnTypes<TEntity>(ModelBuilder modelBuilder) where TEntity : class
......@@ -199,6 +205,10 @@ public class MappedDataTypes
// Composite
//public SomeComposite SomeComposite { get; set; }
// Array
public int[] PrimitiveArray { get; set; }
public PhysicalAddress[] NonPrimitiveArray { get; set; }
}
public class MappedSizedDataTypes
......@@ -277,6 +287,10 @@ public class MappedNullableDataTypes
// Composite
//public SomeComposite SomeComposite { get; set; }
// Array
public int[] PrimitiveArray { get; set; }
public PhysicalAddress[] NonPrimitiveArray { get; set; }
}
/*
......
......@@ -52,6 +52,9 @@ public virtual void Can_query_using_any_mapped_data_type()
Hstore = new Dictionary<string, string> { {"a", "b"} },
//SomeComposite = new SomeComposite { SomeNumber = 8, SomeText = "foo" }
PrimitiveArray = new[] { 2, 3 },
NonPrimitiveArray = new[] { PhysicalAddress.Parse("08-00-2B-01-02-03"), PhysicalAddress.Parse("08-00-2B-01-02-04") },
});
Assert.Equal(1, context.SaveChanges());
......@@ -130,6 +133,12 @@ public virtual void Can_query_using_any_mapped_data_type()
//SomeComposite param22 = new SomeComposite { SomeNumber = 8, SomeText = "foo" };
//Assert.Same(entity, context.Set<MappedNullableDataTypes>().Single(e => e.Int == 999 && e.SomeComposite.Equals(param20)));
var param23 = new[] { 2, 3 };
Assert.Same(entity, context.Set<MappedNullableDataTypes>().Single(e => e.Int == 999 && e.PrimitiveArray == param23));
var param24 = new[] { PhysicalAddress.Parse("08-00-2B-01-02-03"), PhysicalAddress.Parse("08-00-2B-01-02-04") };
Assert.Same(entity, context.Set<MappedNullableDataTypes>().Single(e => e.Int == 999 && e.NonPrimitiveArray == param24));
}
}
......@@ -216,6 +225,12 @@ public virtual void Can_query_using_any_mapped_data_types_with_nulls()
//SomeComposite param22 = null;
//Assert.Same(entity, context.Set<MappedNullableDataTypes>().Single(e => e.Int == 911 && e.SomeComposite == param20));
int[] param23 = null;
Assert.Same(entity, context.Set<MappedNullableDataTypes>().Single(e => e.Int == 911 && e.PrimitiveArray == param23));
PhysicalAddress[] param24 = null;
Assert.Same(entity, context.Set<MappedNullableDataTypes>().Single(e => e.Int == 911 && e.NonPrimitiveArray == param24));
}
}
......@@ -254,8 +269,10 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types()
Jsonb = @"{""a"": ""b""}",
Hstore = new Dictionary<string, string> { { "a", "b" } },
//SomeComposite = new SomeComposite { SomeNumber = 8, SomeText = "foo" }
});
//SomeComposite = new SomeComposite { SomeNumber = 8, SomeText = "foo" }
PrimitiveArray = new[] { 2, 3 },
NonPrimitiveArray = new[] { PhysicalAddress.Parse("08-00-2B-01-02-03"), PhysicalAddress.Parse("08-00-2B-01-02-04") },
});
Assert.Equal(1, context.SaveChanges());
}
......@@ -293,6 +310,8 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types()
Assert.Equal(new Dictionary<string, string> { { "a", "b" } }, entity.Hstore);
//Assert.Equal(new SomeComposite { SomeNumber = 8, SomeText = "foo" }, entity.SomeComposite);
Assert.Equal(new[] { 2, 3 }, entity.PrimitiveArray);
Assert.Equal(new[] { PhysicalAddress.Parse("08-00-2B-01-02-03"), PhysicalAddress.Parse("08-00-2B-01-02-04") }, entity.NonPrimitiveArray);
}
}
......@@ -332,6 +351,8 @@ public virtual void Can_insert_and_read_back_all_mapped_nullable_data_types()
Hstore = new Dictionary<string, string> { { "a", "b" } },
//SomeComposite = new SomeComposite { SomeNumber = 8, SomeText = "foo" }
PrimitiveArray = new[] { 2, 3 },
NonPrimitiveArray = new[] { PhysicalAddress.Parse("08-00-2B-01-02-03"), PhysicalAddress.Parse("08-00-2B-01-02-04") },
});
Assert.Equal(1, context.SaveChanges());
......@@ -369,6 +390,9 @@ public virtual void Can_insert_and_read_back_all_mapped_nullable_data_types()
Assert.Equal(new Dictionary<string, string> { { "a", "b" } }, entity.Hstore);
//Assert.Equal(new SomeComposite { SomeNumber = 8, SomeText = "foo" }, entity.SomeComposite);
Assert.Equal(new[] { 2, 3 }, entity.PrimitiveArray);
Assert.Equal(new[] { PhysicalAddress.Parse("08-00-2B-01-02-03"), PhysicalAddress.Parse("08-00-2B-01-02-04") }, entity.NonPrimitiveArray);
}
}
......@@ -417,6 +441,8 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_set_to_null()
Assert.Null(entity.Hstore);
//Assert.Null(entity.SomeComposite);
Assert.Null(entity.PrimitiveArray);
Assert.Null(entity.NonPrimitiveArray);
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册