diff --git a/src/EFCore.Relational/Storage/HalfTypeMapping.cs b/src/EFCore.Relational/Storage/HalfTypeMapping.cs
new file mode 100644
index 00000000000..85b6d74f68a
--- /dev/null
+++ b/src/EFCore.Relational/Storage/HalfTypeMapping.cs
@@ -0,0 +1,61 @@
+// 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 System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore.Storage.Json;
+
+namespace Microsoft.EntityFrameworkCore.Storage
+{
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ ///
+ /// 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 HalfTypeMapping Default { get; } = new("REAL");
+
+ ///
+ /// 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 HalfTypeMapping(
+ string storeType,
+ DbType? dbType = System.Data.DbType.Single)
+ : base(storeType, typeof(Half), dbType, jsonValueReaderWriter: JsonHalfReaderWriter.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.
+ ///
+ protected HalfTypeMapping(RelationalTypeMappingParameters parameters) : base(parameters) { }
+
+ ///
+ protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) => new HalfTypeMapping(parameters);
+ }
+}
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..100ca29b2b9
--- /dev/null
+++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteHalfTypeMapping.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 SqliteHalfTypeMapping : 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 SqliteHalfTypeMapping 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 SqliteHalfTypeMapping(
+ 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 SqliteHalfTypeMapping(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 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 GenerateNonNullSqlLiteral(object value)
+ => new DoubleTypeMapping(StoreType).GenerateSqlLiteral((double)(Half)value);
+ }
+}
diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs
index 8e9e80978f0..4e16918ad23 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 HalfTypeMapping(RealTypeName) },
+#endif
{ typeof(Guid), SqliteGuidTypeMapping.Default },
{ typeof(JsonElement), SqliteJsonTypeMapping.Default }
};
diff --git a/src/EFCore/Storage/Json/JsonHalfReaderWriter.cs b/src/EFCore/Storage/Json/JsonHalfReaderWriter.cs
new file mode 100644
index 00000000000..72901575940
--- /dev/null
+++ b/src/EFCore/Storage/Json/JsonHalfReaderWriter.cs
@@ -0,0 +1,43 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Globalization;
+using System.Text.Json;
+
+namespace Microsoft.EntityFrameworkCore.Storage.Json
+{
+ ///
+ /// Reads and writes JSON for Half type values.
+ ///
+ public sealed class JsonHalfReaderWriter : JsonValueReaderWriter
+ {
+ private const string HalfFormatConst = "{0:0.0###}";
+
+ 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
+ /// 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 JsonHalfReaderWriter Instance { get; } = new();
+
+ private JsonHalfReaderWriter()
+ {
+
+ }
+
+ ///
+ 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));
+ }
+}
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..5ed78203a80 100644
--- a/src/Microsoft.Data.Sqlite.Core/SqliteValueReader.cs
+++ b/src/Microsoft.Data.Sqlite.Core/SqliteValueReader.cs
@@ -99,6 +99,11 @@ 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 +208,13 @@ 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/SqliteTypeMappingSourceTest.cs b/test/EFCore.Sqlite.Tests/Storage/SqliteTypeMappingSourceTest.cs
index 30cc206a72e..26ab64ead6b 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 f51b7d0733e..2eac229e7b3 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(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);