Skip to content

Commit

Permalink
Read double as float on SQL Server when precision is less than 25 (#2…
Browse files Browse the repository at this point in the history
  • Loading branch information
ajcvickers authored Sep 8, 2021
1 parent b5fc849 commit fcd17f3
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System;
using System.Data;
using System.Data.Common;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal
Expand All @@ -16,6 +18,9 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal
/// </summary>
public class SqlServerDoubleTypeMapping : DoubleTypeMapping
{
private static readonly MethodInfo _getFloatMethod
= typeof(DbDataReader).GetRuntimeMethod(nameof(DbDataReader.GetFloat), new[] { typeof(int) })!;

/// <summary>
/// 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
Expand Down Expand Up @@ -73,6 +78,30 @@ protected override string GenerateNonNullSqlLiteral(object value)
: literal;
}

/// <summary>
/// The method to use when reading values of the given type. The method must be defined
/// on <see cref="DbDataReader" /> or one of its subclasses.
/// </summary>
/// <returns> The method to use to read the value. </returns>
public override MethodInfo GetDataReaderMethod()
=> Precision is <= 24 ? _getFloatMethod : base.GetDataReaderMethod();

/// <summary>
/// Gets a custom expression tree for reading the value from the input data reader
/// expression that contains the database value.
/// </summary>
/// <param name="expression"> The input expression, containing the database value. </param>
/// <returns> The expression with customization added. </returns>
public override Expression CustomizeDataReaderExpression(Expression expression)
{
if (Precision is <= 24)
{
expression = Expression.Convert(expression, typeof(double));
}

return base.CustomizeDataReaderExpression(expression);
}

/// <summary>
/// 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1693,6 +1693,45 @@ private static MappedScaledSeparatelyDataTypes CreateMappedScaledSeparatelyDataT
DecimalAsNumeric3 = 103m
};

[ConditionalFact]
public virtual void Can_insert_and_read_back_double_types_with_precision()
{
using (var context = CreateContext())
{
context.Set<DoubleDataTypes>().Add(CreateDoubleDataTypes(77));

Assert.Equal(1, context.SaveChanges());
}

var parameters = DumpParameters();
Assert.Equal(
@"@p0='77'
@p1='83.33000183105469' (Size = 25)
@p2='83.30000305175781' (Size = 3)",
parameters,
ignoreLineEndingDifferences: true);

using (var context = CreateContext())
{
AssertDoubleDataTypes(context.Set<DoubleDataTypes>().Single(e => e.Id == 77), 77);
}
}

private static void AssertDoubleDataTypes(DoubleDataTypes entity, int id)
{
Assert.Equal(id, entity.Id);
Assert.Equal(83.3f, entity.Double3);
Assert.Equal(83.33f, entity.Double25);
}

private static DoubleDataTypes CreateDoubleDataTypes(int id)
=> new()
{
Id = id,
Double3 = 83.3f,
Double25 = 83.33f
};

[ConditionalFact]
public virtual void Can_insert_and_read_back_all_mapped_data_types_with_precision_and_scale()
{
Expand Down Expand Up @@ -2965,6 +3004,9 @@ public virtual void Columns_have_expected_data_types()
BuiltInNullableDataTypesShadow.TestString ---> [nullable nvarchar] [MaxLength = -1]
DateTimeEnclosure.DateTimeOffset ---> [nullable datetimeoffset] [Precision = 7]
DateTimeEnclosure.Id ---> [int] [Precision = 10 Scale = 0]
DoubleDataTypes.Double25 ---> [float] [Precision = 53]
DoubleDataTypes.Double3 ---> [real] [Precision = 24]
DoubleDataTypes.Id ---> [int] [Precision = 10 Scale = 0]
EmailTemplate.Id ---> [uniqueidentifier]
EmailTemplate.TemplateType ---> [int] [Precision = 10 Scale = 0]
MappedDataTypes.BoolAsBit ---> [bit]
Expand Down Expand Up @@ -3599,6 +3641,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
b.Property(e => e.DecimalAsNumeric3).HasPrecision(3);
});

modelBuilder.Entity<DoubleDataTypes>(
b =>
{
b.Property(e => e.Id).ValueGeneratedNever();
b.Property(e => e.Double3).HasPrecision(3);
b.Property(e => e.Double25).HasPrecision(25);
});

modelBuilder.Entity<MappedPrecisionAndScaledSeparatelyDataTypes>(
b =>
{
Expand Down Expand Up @@ -4118,6 +4168,14 @@ protected class MappedScaledSeparatelyDataTypes
public decimal DecimalAsNumeric3 { get; set; }
}

protected class DoubleDataTypes
{
public int Id { get; set; }

public double Double3 { get; set; }
public double Double25 { get; set; }
}

protected class MappedPrecisionAndScaledDataTypes
{
public int Id { get; set; }
Expand Down

0 comments on commit fcd17f3

Please sign in to comment.