From f3b7014d310fe53c3042878ee6014944c3a7fd6e Mon Sep 17 00:00:00 2001 From: Yehor Kadeniuk <91326774+newmasterSG@users.noreply.github.com> Date: Mon, 25 Nov 2024 00:09:29 +0200 Subject: [PATCH 1/7] Adding Half type support for SqlLite --- .../Internal/SqliteDatabaseModelFactory.cs | 16 ++++++ .../Storage/Internal/SqliteHalfTypeMapping.cs | 49 +++++++++++++++++++ .../Internal/SqliteTypeMappingSource.cs | 3 ++ .../SqliteValueBinder.cs | 10 ++++ .../SqliteValueReader.cs | 10 ++++ .../Storage/SqliteTypeMappingTest.cs | 1 + 6 files changed, 89 insertions(+) create mode 100644 src/EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.cs diff --git a/src/EFCore.Sqlite.Core/Scaffolding/Internal/SqliteDatabaseModelFactory.cs b/src/EFCore.Sqlite.Core/Scaffolding/Internal/SqliteDatabaseModelFactory.cs index b67f8571b42..39469544edc 100644 --- a/src/EFCore.Sqlite.Core/Scaffolding/Internal/SqliteDatabaseModelFactory.cs +++ b/src/EFCore.Sqlite.Core/Scaffolding/Internal/SqliteDatabaseModelFactory.cs @@ -87,6 +87,8 @@ public class SqliteDatabaseModelFactory : DatabaseModelFactory private static readonly HashSet _floatTypes = new(StringComparer.OrdinalIgnoreCase) { "SINGLE" }; + private static readonly HashSet _halfTypes = new(StringComparer.OrdinalIgnoreCase) { "HALF" }; + private static readonly HashSet _decimalTypes = new(StringComparer.OrdinalIgnoreCase) { "DECIMAL" }; private static readonly HashSet _ushortTypes = new(StringComparer.OrdinalIgnoreCase) @@ -127,6 +129,7 @@ public class SqliteDatabaseModelFactory : DatabaseModelFactory .Concat(_shortTypes.Select(t => KeyValuePair.Create(t, typeof(short)))) .Concat(_sbyteTypes.Select(t => KeyValuePair.Create(t, typeof(sbyte)))) .Concat(_floatTypes.Select(t => KeyValuePair.Create(t, typeof(float)))) + .Concat(_halfTypes.Select(t => KeyValuePair.Create(t, typeof(Half)))) .Concat(_decimalTypes.Select(t => KeyValuePair.Create(t, typeof(decimal)))) .Concat(_timeOnlyTypes.Select(t => KeyValuePair.Create(t, typeof(TimeOnly)))) .Concat(_ushortTypes.Select(t => KeyValuePair.Create(t, typeof(ushort)))) @@ -811,6 +814,19 @@ protected virtual void InferClrTypes(DbConnection connection, DatabaseTable tabl _logger.OutOfRangeWarning(column.Name, table.Name, "float"); } + if (_halfTypes.Contains(baseColumnType)) + { + if (min >= (double)Half.MinValue + && max <= (double)Half.MaxValue) + { + column["ClrType"] = typeof(Half); + + continue; + } + + _logger.OutOfRangeWarning(column.Name, table.Name, "Half"); + } + if (_decimalTypes.Contains(baseColumnType)) { column["ClrType"] = typeof(decimal); diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.cs new file mode 100644 index 00000000000..e3c18c0fba1 --- /dev/null +++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System.Data; +using Microsoft.EntityFrameworkCore.Sqlite.Storage.Json.Internal; + +namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.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 class SqliteHalfTypeMapping : RelationalTypeMapping + { + private const string HalfFormatConst = "{0:0.0####}"; + + /// + /// 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 SqliteHalfTypeMapping () : base("REAL", typeof(Half)) { } + + /// + /// 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. + /// + protected SqliteHalfTypeMapping(RelationalTypeMappingParameters parameters) : base(parameters) { } + + /// + protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) => new SqliteHalfTypeMapping(parameters); + + /// + /// 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. + /// + protected override string SqlLiteralFormatString + => "'" + HalfFormatConst + "'"; + + /// + protected override string GenerateNonNullSqlLiteral(object value) => Convert.ToDouble((Half)value).ToString("R"); + } +} diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs index 8e9e80978f0..1b40c3b9271 100644 --- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs +++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs @@ -82,6 +82,9 @@ private static readonly HashSet SpatialiteTypes { typeof(decimal), SqliteDecimalTypeMapping.Default }, { typeof(double), Real }, { typeof(float), new FloatTypeMapping(RealTypeName) }, +#if NET5_0_OR_GREATER + { typeof(Half), new FloatTypeMapping(RealTypeName) }, +#endif { typeof(Guid), SqliteGuidTypeMapping.Default }, { typeof(JsonElement), SqliteJsonTypeMapping.Default } }; diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteValueBinder.cs b/src/Microsoft.Data.Sqlite.Core/SqliteValueBinder.cs index b1908031a13..58735ced8a3 100644 --- a/src/Microsoft.Data.Sqlite.Core/SqliteValueBinder.cs +++ b/src/Microsoft.Data.Sqlite.Core/SqliteValueBinder.cs @@ -154,6 +154,13 @@ public virtual void Bind() var value1 = (double)(float)value; BindDouble(value1); } +#if NET5_0_OR_GREATER + else if (type == typeof(Half)) + { + var value1 = (double)(Half)value; + BindDouble(value1); + } +#endif else if (type == typeof(Guid)) { var guid = (Guid)value; @@ -245,6 +252,9 @@ public virtual void Bind() { typeof(decimal), SqliteType.Text }, { typeof(double), SqliteType.Real }, { typeof(float), SqliteType.Real }, +#if NET5_0_OR_GREATER + { typeof(Half), SqliteType.Real }, +#endif { typeof(Guid), SqliteType.Text }, { typeof(int), SqliteType.Integer }, { typeof(long), SqliteType.Integer }, diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteValueReader.cs b/src/Microsoft.Data.Sqlite.Core/SqliteValueReader.cs index 8ea8d3ac24b..d8bd4c1d764 100644 --- a/src/Microsoft.Data.Sqlite.Core/SqliteValueReader.cs +++ b/src/Microsoft.Data.Sqlite.Core/SqliteValueReader.cs @@ -99,6 +99,10 @@ public virtual double GetDouble(int ordinal) public virtual float GetFloat(int ordinal) => (float)GetDouble(ordinal); +#if NET5_0_OR_GREATER + public virtual Half GetHalf(int ordinal) + => (Half)GetDouble(ordinal); +#endif public virtual Guid GetGuid(int ordinal) { var sqliteType = GetSqliteType(ordinal); @@ -203,6 +207,12 @@ public virtual string GetString(int ordinal) return (T)(object)GetFloat(ordinal); } +#if NET5_0_OR_GREATER + if (typeof(T) == typeof(Half)) + { + return (T)(object)GetHalf(ordinal); + } +#endif if (typeof(T) == typeof(Guid)) { return (T)(object)GetGuid(ordinal); diff --git a/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingTest.cs b/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingTest.cs index f51b7d0733e..4bbe1b37ca7 100644 --- a/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingTest.cs +++ b/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingTest.cs @@ -72,6 +72,7 @@ protected override DbCommand CreateTestCommand() [InlineData(typeof(SqliteDecimalTypeMapping), typeof(decimal))] [InlineData(typeof(SqliteGuidTypeMapping), typeof(Guid))] [InlineData(typeof(SqliteULongTypeMapping), typeof(ulong))] + [InlineData(typeof(SqliteHalfTypeMapping), typeof(Half))] public override void Create_and_clone_with_converter(Type mappingType, Type type) => base.Create_and_clone_with_converter(mappingType, type); From 9e92723614b565c1cd521a546555dd24d9258e60 Mon Sep 17 00:00:00 2001 From: Yehor Kadeniuk <91326774+newmasterSG@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:18:45 +0200 Subject: [PATCH 2/7] Update src/Microsoft.Data.Sqlite.Core/SqliteValueReader.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jiri Cincura ↹ --- src/Microsoft.Data.Sqlite.Core/SqliteValueReader.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteValueReader.cs b/src/Microsoft.Data.Sqlite.Core/SqliteValueReader.cs index d8bd4c1d764..9c171c83d31 100644 --- a/src/Microsoft.Data.Sqlite.Core/SqliteValueReader.cs +++ b/src/Microsoft.Data.Sqlite.Core/SqliteValueReader.cs @@ -103,6 +103,7 @@ public virtual float GetFloat(int ordinal) public virtual Half GetHalf(int ordinal) => (Half)GetDouble(ordinal); #endif + public virtual Guid GetGuid(int ordinal) { var sqliteType = GetSqliteType(ordinal); From 59d7712e46432c06efbbaa6a7ee8351a12f4ce57 Mon Sep 17 00:00:00 2001 From: Yehor Kadeniuk <91326774+newmasterSG@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:49:07 +0200 Subject: [PATCH 3/7] change precision of half type and delete GenerateNonNullSqlLiteral method --- .../Storage/Internal/SqliteHalfTypeMapping.cs | 5 +---- src/Microsoft.Data.Sqlite.Core/SqliteValueReader.cs | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.cs index e3c18c0fba1..d67dd7f2155 100644 --- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.cs +++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.cs @@ -13,7 +13,7 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal /// public class SqliteHalfTypeMapping : RelationalTypeMapping { - private const string HalfFormatConst = "{0:0.0####}"; + private const string HalfFormatConst = "{0:0.0###}"; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -42,8 +42,5 @@ protected SqliteHalfTypeMapping(RelationalTypeMappingParameters parameters) : ba /// protected override string SqlLiteralFormatString => "'" + HalfFormatConst + "'"; - - /// - protected override string GenerateNonNullSqlLiteral(object value) => Convert.ToDouble((Half)value).ToString("R"); } } diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteValueReader.cs b/src/Microsoft.Data.Sqlite.Core/SqliteValueReader.cs index 9c171c83d31..5ed78203a80 100644 --- a/src/Microsoft.Data.Sqlite.Core/SqliteValueReader.cs +++ b/src/Microsoft.Data.Sqlite.Core/SqliteValueReader.cs @@ -214,6 +214,7 @@ public virtual string GetString(int ordinal) return (T)(object)GetHalf(ordinal); } #endif + if (typeof(T) == typeof(Guid)) { return (T)(object)GetGuid(ordinal); From bdf52d261cf5e73ff9a46fb64507ba40dd5af79b Mon Sep 17 00:00:00 2001 From: Yehor Kadeniuk <91326774+newmasterSG@users.noreply.github.com> Date: Thu, 5 Dec 2024 00:10:20 +0200 Subject: [PATCH 4/7] Change mapping source for half type also refactor type mapping also create json reader writer --- .../Storage/Internal/SqliteHalfTypeMapping.cs | 24 +++++--- .../Internal/SqliteTypeMappingSource.cs | 2 +- .../Internal/SqliteJsonHalfReaderWriter.cs | 56 +++++++++++++++++++ 3 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 src/EFCore.Sqlite.Core/Storage/Json/Internal/SqliteJsonHalfReaderWriter.cs diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.cs index d67dd7f2155..5314a267660 100644 --- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.cs +++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Data; using Microsoft.EntityFrameworkCore.Sqlite.Storage.Json.Internal; +using Microsoft.EntityFrameworkCore.Storage.Json; namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal { @@ -13,7 +14,6 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal /// public class SqliteHalfTypeMapping : RelationalTypeMapping { - private const string HalfFormatConst = "{0:0.0###}"; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -21,7 +21,7 @@ public class SqliteHalfTypeMapping : RelationalTypeMapping /// 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 SqliteHalfTypeMapping () : base("REAL", typeof(Half)) { } + public static SqliteHalfTypeMapping Default { get; } = new("REAL"); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -29,10 +29,16 @@ public SqliteHalfTypeMapping () : base("REAL", typeof(Half)) { } /// 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. /// - protected SqliteHalfTypeMapping(RelationalTypeMappingParameters parameters) : base(parameters) { } - - /// - protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) => new SqliteHalfTypeMapping(parameters); + public SqliteHalfTypeMapping( + string storeType, + DbType? dbType = System.Data.DbType.Single) + : this(new RelationalTypeMappingParameters( + new CoreTypeMappingParameters( + typeof(Half), jsonValueReaderWriter: SqliteJsonHalfReaderWriter.Instance), + storeType, + dbType: dbType)) + { + } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -40,7 +46,9 @@ protected SqliteHalfTypeMapping(RelationalTypeMappingParameters parameters) : ba /// 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. /// - protected override string SqlLiteralFormatString - => "'" + HalfFormatConst + "'"; + protected SqliteHalfTypeMapping(RelationalTypeMappingParameters parameters) : base(parameters) { } + + /// + protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) => new SqliteHalfTypeMapping(parameters); } } diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs index 1b40c3b9271..96bb586cf1a 100644 --- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs +++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs @@ -83,7 +83,7 @@ private static readonly HashSet SpatialiteTypes { typeof(double), Real }, { typeof(float), new FloatTypeMapping(RealTypeName) }, #if NET5_0_OR_GREATER - { typeof(Half), new FloatTypeMapping(RealTypeName) }, + { typeof(Half), new SqliteHalfTypeMapping(RealTypeName) }, #endif { typeof(Guid), SqliteGuidTypeMapping.Default }, { typeof(JsonElement), SqliteJsonTypeMapping.Default } diff --git a/src/EFCore.Sqlite.Core/Storage/Json/Internal/SqliteJsonHalfReaderWriter.cs b/src/EFCore.Sqlite.Core/Storage/Json/Internal/SqliteJsonHalfReaderWriter.cs new file mode 100644 index 00000000000..830084400a0 --- /dev/null +++ b/src/EFCore.Sqlite.Core/Storage/Json/Internal/SqliteJsonHalfReaderWriter.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Storage.Json; + +namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Json.Internal +{ + /// + /// The Sqlite-specific JsonValueReaderWrite for Half. Generates a string representation instead of a JSON number, in order to match + /// our SQLite non-JSON representation. + /// + /// + /// 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 SqliteJsonHalfReaderWriter : JsonValueReaderWriter + { + private const string HalfFormatConst = "{0:0.0###}"; + + private static readonly PropertyInfo InstanceProperty = typeof(SqliteJsonHalfReaderWriter).GetProperty(nameof(Instance))!; + + /// + /// 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 static SqliteJsonHalfReaderWriter Instance { get; } = new(); + + private SqliteJsonHalfReaderWriter() + { + + } + + /// + public override Expression ConstructorExpression + => Expression.Property(null, InstanceProperty); + + /// + public override Half FromJsonTyped(ref Utf8JsonReaderManager manager, object? existingObject = null) => + Half.Parse(manager.CurrentReader.GetString()!, CultureInfo.InvariantCulture); + + /// + public override void ToJsonTyped(Utf8JsonWriter writer, Half value) => + writer.WriteStringValue(string.Format(CultureInfo.InvariantCulture, HalfFormatConst, value)); + } +} From ce482825cf646bd618af0f050428c6057c31d0ce Mon Sep 17 00:00:00 2001 From: Yehor Kadeniuk <91326774+newmasterSG@users.noreply.github.com> Date: Thu, 5 Dec 2024 09:06:56 +0200 Subject: [PATCH 5/7] re write logic to simple half type mapping --- .../Storage/HalfTypeMapping.cs} | 37 ++++++++++++------- .../Internal/SqliteTypeMappingSource.cs | 2 +- .../Storage/Json/JsonHalfReaderWriter.cs} | 25 +++---------- .../Storage/SqliteTypeMappingTest.cs | 2 +- 4 files changed, 32 insertions(+), 34 deletions(-) rename src/{EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.cs => EFCore.Relational/Storage/HalfTypeMapping.cs} (65%) rename src/{EFCore.Sqlite.Core/Storage/Json/Internal/SqliteJsonHalfReaderWriter.cs => EFCore/Storage/Json/JsonHalfReaderWriter.cs} (55%) diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.cs b/src/EFCore.Relational/Storage/HalfTypeMapping.cs similarity index 65% rename from src/EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.cs rename to src/EFCore.Relational/Storage/HalfTypeMapping.cs index 5314a267660..c6f469c82d8 100644 --- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.cs +++ b/src/EFCore.Relational/Storage/HalfTypeMapping.cs @@ -1,27 +1,38 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; using System.Data; -using Microsoft.EntityFrameworkCore.Sqlite.Storage.Json.Internal; +using System.Linq; +using System.Text; +using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.Storage.Json; -namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal +namespace Microsoft.EntityFrameworkCore.Storage { /// - /// 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. + /// + /// Represents the mapping between a .NET type and a database type. + /// + /// + /// This type is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// /// - public class SqliteHalfTypeMapping : RelationalTypeMapping + /// + /// See Implementation of database providers and extensions + /// for more information and examples. + /// + public class HalfTypeMapping : RelationalTypeMapping { - /// /// 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 static SqliteHalfTypeMapping Default { get; } = new("REAL"); + public static HalfTypeMapping Default { get; } = new("REAL"); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -29,12 +40,12 @@ public class SqliteHalfTypeMapping : RelationalTypeMapping /// 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 SqliteHalfTypeMapping( + public HalfTypeMapping( string storeType, DbType? dbType = System.Data.DbType.Single) : this(new RelationalTypeMappingParameters( new CoreTypeMappingParameters( - typeof(Half), jsonValueReaderWriter: SqliteJsonHalfReaderWriter.Instance), + typeof(Half), jsonValueReaderWriter: JsonHalfReaderWriter.Instance), storeType, dbType: dbType)) { @@ -46,9 +57,9 @@ public SqliteHalfTypeMapping( /// 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. /// - protected SqliteHalfTypeMapping(RelationalTypeMappingParameters parameters) : base(parameters) { } + protected HalfTypeMapping(RelationalTypeMappingParameters parameters) : base(parameters) { } /// - protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) => new SqliteHalfTypeMapping(parameters); + protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) => new HalfTypeMapping(parameters); } } diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs index 96bb586cf1a..4e16918ad23 100644 --- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs +++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs @@ -83,7 +83,7 @@ private static readonly HashSet SpatialiteTypes { typeof(double), Real }, { typeof(float), new FloatTypeMapping(RealTypeName) }, #if NET5_0_OR_GREATER - { typeof(Half), new SqliteHalfTypeMapping(RealTypeName) }, + { typeof(Half), new HalfTypeMapping(RealTypeName) }, #endif { typeof(Guid), SqliteGuidTypeMapping.Default }, { typeof(JsonElement), SqliteJsonTypeMapping.Default } diff --git a/src/EFCore.Sqlite.Core/Storage/Json/Internal/SqliteJsonHalfReaderWriter.cs b/src/EFCore/Storage/Json/JsonHalfReaderWriter.cs similarity index 55% rename from src/EFCore.Sqlite.Core/Storage/Json/Internal/SqliteJsonHalfReaderWriter.cs rename to src/EFCore/Storage/Json/JsonHalfReaderWriter.cs index 830084400a0..72901575940 100644 --- a/src/EFCore.Sqlite.Core/Storage/Json/Internal/SqliteJsonHalfReaderWriter.cs +++ b/src/EFCore/Storage/Json/JsonHalfReaderWriter.cs @@ -1,32 +1,19 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; using System.Globalization; -using System.Linq; -using System.Text; using System.Text.Json; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore.Storage.Json; -namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Json.Internal +namespace Microsoft.EntityFrameworkCore.Storage.Json { /// - /// The Sqlite-specific JsonValueReaderWrite for Half. Generates a string representation instead of a JSON number, in order to match - /// our SQLite non-JSON representation. + /// Reads and writes JSON for Half type values. /// - /// - /// 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 SqliteJsonHalfReaderWriter : JsonValueReaderWriter + public sealed class JsonHalfReaderWriter : JsonValueReaderWriter { private const string HalfFormatConst = "{0:0.0###}"; - private static readonly PropertyInfo InstanceProperty = typeof(SqliteJsonHalfReaderWriter).GetProperty(nameof(Instance))!; + private static readonly PropertyInfo InstanceProperty = typeof(JsonHalfReaderWriter).GetProperty(nameof(Instance))!; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -34,9 +21,9 @@ public sealed class SqliteJsonHalfReaderWriter : JsonValueReaderWriter /// 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 static SqliteJsonHalfReaderWriter Instance { get; } = new(); + public static JsonHalfReaderWriter Instance { get; } = new(); - private SqliteJsonHalfReaderWriter() + private JsonHalfReaderWriter() { } diff --git a/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingTest.cs b/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingTest.cs index 4bbe1b37ca7..ef71417375d 100644 --- a/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingTest.cs +++ b/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingTest.cs @@ -72,7 +72,7 @@ protected override DbCommand CreateTestCommand() [InlineData(typeof(SqliteDecimalTypeMapping), typeof(decimal))] [InlineData(typeof(SqliteGuidTypeMapping), typeof(Guid))] [InlineData(typeof(SqliteULongTypeMapping), typeof(ulong))] - [InlineData(typeof(SqliteHalfTypeMapping), typeof(Half))] + [InlineData(typeof(HalfTypeMapping), typeof(Half))] public override void Create_and_clone_with_converter(Type mappingType, Type type) => base.Create_and_clone_with_converter(mappingType, type); From 983c5bff238763c05dbec53498f248ee4ed3ee6e Mon Sep 17 00:00:00 2001 From: Yehor Kadeniuk <91326774+newmasterSG@users.noreply.github.com> Date: Fri, 13 Dec 2024 15:43:20 +0200 Subject: [PATCH 6/7] Add double to half convertor and add type mapping --- .../Internal/SqlliteHalfTypeMapping.cs | 65 +++++++++++++++++++ .../ValueConversion/DoubleToHalfConverter.cs | 55 ++++++++++++++++ .../Storage/SqliteTypeMappingSourceTest.cs | 3 + .../Storage/SqliteTypeMappingTest.cs | 2 +- 4 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 src/EFCore.Sqlite.Core/Storage/Internal/SqlliteHalfTypeMapping.cs create mode 100644 src/EFCore/Storage/ValueConversion/DoubleToHalfConverter.cs diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqlliteHalfTypeMapping.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqlliteHalfTypeMapping.cs new file mode 100644 index 00000000000..e85b07dac2e --- /dev/null +++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqlliteHalfTypeMapping.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Data; + +namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.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 class SqlliteHalfTypeMapping : HalfTypeMapping + { + /// + /// 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 static new SqlliteHalfTypeMapping Default { get; } = new(SqliteTypeMappingSource.RealTypeName); + + /// + /// 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 SqlliteHalfTypeMapping( + string storeType, + DbType? dbType = System.Data.DbType.Single) + : base(storeType, dbType) + { + } + + /// + /// 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. + /// + protected SqlliteHalfTypeMapping(RelationalTypeMappingParameters parameters) + : base(parameters) + { + } + + /// + /// Creates a copy of this mapping. + /// + /// The parameters for this mapping. + /// The newly created mapping. + protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) + => new SqlliteHalfTypeMapping(parameters); + + /// + /// 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. + /// + protected override string GenerateNonNullSqlLiteral(object value) + => new DoubleTypeMapping(StoreType).GenerateSqlLiteral((double)(Half)value); + } +} diff --git a/src/EFCore/Storage/ValueConversion/DoubleToHalfConverter.cs b/src/EFCore/Storage/ValueConversion/DoubleToHalfConverter.cs new file mode 100644 index 00000000000..bf1ea677e3c --- /dev/null +++ b/src/EFCore/Storage/ValueConversion/DoubleToHalfConverter.cs @@ -0,0 +1,55 @@ +// 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.Storage.ValueConversion +{ + /// + /// Converts double values to Half type. + /// + /// + /// See EF Core value converters for more information and examples. + /// + public class DoubleToHalfConverter : ValueConverter + { + private static readonly ConverterMappingHints DefaultHints = new(precision: 4); + + /// + /// Creates a new instance of this converter. + /// + /// + /// See EF Core value converters for more information and examples. + /// + public DoubleToHalfConverter() + : this(new()) + { + + } + + /// + /// Creates a new instance of this converter. + /// + /// + /// See EF Core value converters for more information and examples. + /// + /// + /// Hints that can be used by the to create data types with appropriate + /// facets for the converted data. + /// + public DoubleToHalfConverter(ConverterMappingHints mappingHints) + : base( + v => (Half)v!, + v => v.HasValue + ? (double)v.Value + : double.MinValue, + DefaultHints.With(mappingHints)) + { + } + + /// + /// A for the default use of this converter. + /// + public static ValueConverterInfo DefaultInfo { get; } + = new(typeof(double), typeof(Half), i => new DoubleToHalfConverter(i.MappingHints!), DefaultHints); + } +} diff --git a/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingSourceTest.cs b/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingSourceTest.cs index 30cc206a72e..756501afc0c 100644 --- a/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingSourceTest.cs +++ b/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingSourceTest.cs @@ -50,6 +50,7 @@ public class SqliteTypeMappingSourceTest : RelationalTypeMappingSourceTestBase [InlineData("TEXT", typeof(TimeSpan?), DbType.Time)] [InlineData("TEXT", typeof(decimal?), DbType.Decimal)] [InlineData("REAL", typeof(float?), DbType.Single)] + [InlineData("REAL", typeof(Half?), DbType.Single)] [InlineData("REAL", typeof(double?), DbType.Double)] [InlineData("INTEGER", typeof(ByteEnum?), DbType.Byte)] [InlineData("INTEGER", typeof(ShortEnum?), DbType.Int16)] @@ -176,6 +177,7 @@ public void Does_mappings_for_store_type(string storeType, Type clrType, DbType? [InlineData("REAL", typeof(float), DbType.Single)] [InlineData("UNREALISTIC", typeof(float), DbType.Single)] [InlineData("RUBBISH", typeof(float), DbType.Single)] + [InlineData("RUBBISH", typeof(Half?), DbType.Single)] [InlineData("REAL", typeof(double), DbType.Double)] [InlineData("UNREALISTIC", typeof(double), DbType.Double)] [InlineData("RUBBISH", typeof(double), DbType.Double)] @@ -304,6 +306,7 @@ public void Does_default_mappings_for_values() Assert.Equal("TEXT", CreateRelationalTypeMappingSource(model).GetMappingForValue(1.0m).StoreType); Assert.Equal("REAL", CreateRelationalTypeMappingSource(model).GetMappingForValue(1.0).StoreType); Assert.Equal("REAL", CreateRelationalTypeMappingSource(model).GetMappingForValue(1.0f).StoreType); + Assert.Equal("REAL", CreateRelationalTypeMappingSource(model).GetMappingForValue((Half)1.0).StoreType); } [ConditionalFact] diff --git a/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingTest.cs b/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingTest.cs index ef71417375d..f6bbd659eef 100644 --- a/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingTest.cs +++ b/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingTest.cs @@ -72,7 +72,7 @@ protected override DbCommand CreateTestCommand() [InlineData(typeof(SqliteDecimalTypeMapping), typeof(decimal))] [InlineData(typeof(SqliteGuidTypeMapping), typeof(Guid))] [InlineData(typeof(SqliteULongTypeMapping), typeof(ulong))] - [InlineData(typeof(HalfTypeMapping), typeof(Half))] + [InlineData(typeof(SqlliteHalfTypeMapping), typeof(Half))] public override void Create_and_clone_with_converter(Type mappingType, Type type) => base.Create_and_clone_with_converter(mappingType, type); From ca1afda25412c5882bda3da047cc6755cc9a8790 Mon Sep 17 00:00:00 2001 From: Yehor Kadeniuk <91326774+newmasterSG@users.noreply.github.com> Date: Sun, 29 Dec 2024 16:42:43 +0200 Subject: [PATCH 7/7] delete double to float convertor, change naming and constuctor --- .../Storage/HalfTypeMapping.cs | 6 +- ...ypeMapping.cs => SqliteHalfTypeMapping.cs} | 10 ++-- .../ValueConversion/DoubleToHalfConverter.cs | 55 ------------------- .../Storage/SqliteTypeMappingSourceTest.cs | 4 +- .../Storage/SqliteTypeMappingTest.cs | 2 +- 5 files changed, 9 insertions(+), 68 deletions(-) rename src/EFCore.Sqlite.Core/Storage/Internal/{SqlliteHalfTypeMapping.cs => SqliteHalfTypeMapping.cs} (90%) delete mode 100644 src/EFCore/Storage/ValueConversion/DoubleToHalfConverter.cs diff --git a/src/EFCore.Relational/Storage/HalfTypeMapping.cs b/src/EFCore.Relational/Storage/HalfTypeMapping.cs index c6f469c82d8..85b6d74f68a 100644 --- a/src/EFCore.Relational/Storage/HalfTypeMapping.cs +++ b/src/EFCore.Relational/Storage/HalfTypeMapping.cs @@ -43,11 +43,7 @@ public class HalfTypeMapping : RelationalTypeMapping public HalfTypeMapping( string storeType, DbType? dbType = System.Data.DbType.Single) - : this(new RelationalTypeMappingParameters( - new CoreTypeMappingParameters( - typeof(Half), jsonValueReaderWriter: JsonHalfReaderWriter.Instance), - storeType, - dbType: dbType)) + : base(storeType, typeof(Half), dbType, jsonValueReaderWriter: JsonHalfReaderWriter.Instance) { } diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqlliteHalfTypeMapping.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.cs similarity index 90% rename from src/EFCore.Sqlite.Core/Storage/Internal/SqlliteHalfTypeMapping.cs rename to src/EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.cs index e85b07dac2e..100ca29b2b9 100644 --- a/src/EFCore.Sqlite.Core/Storage/Internal/SqlliteHalfTypeMapping.cs +++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal /// 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 class SqlliteHalfTypeMapping : HalfTypeMapping + public class SqliteHalfTypeMapping : HalfTypeMapping { /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -19,7 +19,7 @@ public class SqlliteHalfTypeMapping : HalfTypeMapping /// 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 static new SqlliteHalfTypeMapping Default { get; } = new(SqliteTypeMappingSource.RealTypeName); + public static new SqliteHalfTypeMapping Default { get; } = new(SqliteTypeMappingSource.RealTypeName); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -27,7 +27,7 @@ public class SqlliteHalfTypeMapping : HalfTypeMapping /// 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 SqlliteHalfTypeMapping( + public SqliteHalfTypeMapping( string storeType, DbType? dbType = System.Data.DbType.Single) : base(storeType, dbType) @@ -40,7 +40,7 @@ public SqlliteHalfTypeMapping( /// 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. /// - protected SqlliteHalfTypeMapping(RelationalTypeMappingParameters parameters) + protected SqliteHalfTypeMapping(RelationalTypeMappingParameters parameters) : base(parameters) { } @@ -51,7 +51,7 @@ protected SqlliteHalfTypeMapping(RelationalTypeMappingParameters parameters) /// The parameters for this mapping. /// The newly created mapping. protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) - => new SqlliteHalfTypeMapping(parameters); + => new SqliteHalfTypeMapping(parameters); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore/Storage/ValueConversion/DoubleToHalfConverter.cs b/src/EFCore/Storage/ValueConversion/DoubleToHalfConverter.cs deleted file mode 100644 index bf1ea677e3c..00000000000 --- a/src/EFCore/Storage/ValueConversion/DoubleToHalfConverter.cs +++ /dev/null @@ -1,55 +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.Storage.ValueConversion -{ - /// - /// Converts double values to Half type. - /// - /// - /// See EF Core value converters for more information and examples. - /// - public class DoubleToHalfConverter : ValueConverter - { - private static readonly ConverterMappingHints DefaultHints = new(precision: 4); - - /// - /// Creates a new instance of this converter. - /// - /// - /// See EF Core value converters for more information and examples. - /// - public DoubleToHalfConverter() - : this(new()) - { - - } - - /// - /// Creates a new instance of this converter. - /// - /// - /// See EF Core value converters for more information and examples. - /// - /// - /// Hints that can be used by the to create data types with appropriate - /// facets for the converted data. - /// - public DoubleToHalfConverter(ConverterMappingHints mappingHints) - : base( - v => (Half)v!, - v => v.HasValue - ? (double)v.Value - : double.MinValue, - DefaultHints.With(mappingHints)) - { - } - - /// - /// A for the default use of this converter. - /// - public static ValueConverterInfo DefaultInfo { get; } - = new(typeof(double), typeof(Half), i => new DoubleToHalfConverter(i.MappingHints!), DefaultHints); - } -} diff --git a/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingSourceTest.cs b/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingSourceTest.cs index 756501afc0c..26ab64ead6b 100644 --- a/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingSourceTest.cs +++ b/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingSourceTest.cs @@ -50,7 +50,7 @@ public class SqliteTypeMappingSourceTest : RelationalTypeMappingSourceTestBase [InlineData("TEXT", typeof(TimeSpan?), DbType.Time)] [InlineData("TEXT", typeof(decimal?), DbType.Decimal)] [InlineData("REAL", typeof(float?), DbType.Single)] - [InlineData("REAL", typeof(Half?), DbType.Single)] + [InlineData("REAL", typeof(Half), DbType.Single)] [InlineData("REAL", typeof(double?), DbType.Double)] [InlineData("INTEGER", typeof(ByteEnum?), DbType.Byte)] [InlineData("INTEGER", typeof(ShortEnum?), DbType.Int16)] @@ -177,7 +177,7 @@ public void Does_mappings_for_store_type(string storeType, Type clrType, DbType? [InlineData("REAL", typeof(float), DbType.Single)] [InlineData("UNREALISTIC", typeof(float), DbType.Single)] [InlineData("RUBBISH", typeof(float), DbType.Single)] - [InlineData("RUBBISH", typeof(Half?), DbType.Single)] + [InlineData("RUBBISH", typeof(Half), DbType.Single)] [InlineData("REAL", typeof(double), DbType.Double)] [InlineData("UNREALISTIC", typeof(double), DbType.Double)] [InlineData("RUBBISH", typeof(double), DbType.Double)] diff --git a/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingTest.cs b/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingTest.cs index f6bbd659eef..2eac229e7b3 100644 --- a/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingTest.cs +++ b/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingTest.cs @@ -72,7 +72,7 @@ protected override DbCommand CreateTestCommand() [InlineData(typeof(SqliteDecimalTypeMapping), typeof(decimal))] [InlineData(typeof(SqliteGuidTypeMapping), typeof(Guid))] [InlineData(typeof(SqliteULongTypeMapping), typeof(ulong))] - [InlineData(typeof(SqlliteHalfTypeMapping), typeof(Half))] + [InlineData(typeof(EntityFrameworkCore.Sqlite.Storage.Internal.SqliteHalfTypeMapping), typeof(Half))] public override void Create_and_clone_with_converter(Type mappingType, Type type) => base.Create_and_clone_with_converter(mappingType, type);