Skip to content

Commit 5013f47

Browse files
add tests and fix dotnet#26742
test and fix
1 parent 6da773f commit 5013f47

11 files changed

+549
-30
lines changed

src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeOffsetTypeMapping.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ protected override void ConfigureParameter(DbParameter parameter)
110110

111111
if (Precision.HasValue)
112112
{
113-
parameter.Precision = unchecked((byte)Precision.Value);
113+
// Workaround for inconsistant definition of precision/scale between EF and SQLClient for VarTime types
114+
parameter.Scale = unchecked((byte)Precision.Value);
114115
}
115116
}
116117
}

src/EFCore.SqlServer/Storage/Internal/SqlServerDateTimeTypeMapping.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class SqlServerDateTimeTypeMapping : DateTimeTypeMapping
2525
// so the order of the entries in this array is important
2626
private readonly string[] _dateTime2Formats =
2727
{
28-
"'{0:yyyy-MM-ddTHH:mm:ss}'",
28+
"'{0:yyyy-MM-ddTHH:mm:ssK}'",
2929
"'{0:yyyy-MM-ddTHH:mm:ss.fK}'",
3030
"'{0:yyyy-MM-ddTHH:mm:ss.ffK}'",
3131
"'{0:yyyy-MM-ddTHH:mm:ss.fffK}'",
@@ -89,7 +89,8 @@ protected override void ConfigureParameter(DbParameter parameter)
8989

9090
if (Precision.HasValue)
9191
{
92-
parameter.Precision = unchecked((byte)Precision.Value);
92+
// Workaround for inconsistant definition of precision/scale between EF and SQLClient for VarTime types
93+
parameter.Scale = unchecked((byte)Precision.Value);
9394
}
9495
}
9596

@@ -133,6 +134,6 @@ protected override string SqlLiteralFormatString
133134
return _dateTime2Formats[7];
134135
}
135136
}
136-
}
137+
}
137138
}
138139
}

src/EFCore.SqlServer/Storage/Internal/SqlServerTimeSpanTypeMapping.cs

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System;
45
using System.Data;
56
using System.Data.Common;
7+
using System.Globalization;
68
using Microsoft.Data.SqlClient;
79
using Microsoft.EntityFrameworkCore.Storage;
810

@@ -16,14 +18,36 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal
1618
/// </summary>
1719
public class SqlServerTimeSpanTypeMapping : TimeSpanTypeMapping
1820
{
21+
// Note: this array will be accessed using the precision as an index
22+
// so the order of the entries in this array is important
23+
private readonly string[] _timeFormats =
24+
{
25+
@"'{0:hh\:mm\:ss}'",
26+
@"'{0:hh\:mm\:ss\.F}'",
27+
@"'{0:hh\:mm\:ss\.FF}'",
28+
@"'{0:hh\:mm\:ss\.FFF}'",
29+
@"'{0:hh\:mm\:ss\.FFFF}'",
30+
@"'{0:hh\:mm\:ss\.FFFFF}'",
31+
@"'{0:hh\:mm\:ss\.FFFFFF}'",
32+
@"'{0:hh\:mm\:ss\.FFFFFFF}'"
33+
};
34+
1935
/// <summary>
2036
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
2137
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
2238
/// any release. You should only use it directly in your code with extreme caution and knowing that
2339
/// doing so can result in application failures when updating to a new Entity Framework Core release.
2440
/// </summary>
25-
public SqlServerTimeSpanTypeMapping(string storeType, DbType? dbType = System.Data.DbType.Time)
26-
: base(storeType, dbType)
41+
public SqlServerTimeSpanTypeMapping(
42+
string storeType,
43+
DbType? dbType = System.Data.DbType.Time,
44+
StoreTypePostfix storeTypePostfix = StoreTypePostfix.Precision)
45+
: base(
46+
new RelationalTypeMappingParameters(
47+
new CoreTypeMappingParameters(typeof(TimeSpan)),
48+
storeType,
49+
storeTypePostfix,
50+
dbType))
2751
{
2852
}
2953

@@ -61,6 +85,48 @@ protected override void ConfigureParameter(DbParameter parameter)
6185
{
6286
((SqlParameter)parameter).SqlDbType = SqlDbType.Time;
6387
}
88+
if (Precision.HasValue)
89+
{
90+
parameter.Scale = unchecked((byte)Precision.Value);
91+
}
92+
}
93+
94+
/// <summary>
95+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
96+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
97+
/// any release. You should only use it directly in your code with extreme caution and knowing that
98+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
99+
/// </summary>
100+
protected override string SqlLiteralFormatString
101+
{
102+
get
103+
{
104+
if (Precision.HasValue)
105+
{
106+
var precision = Precision.Value;
107+
if (precision <= 7
108+
&& precision >= 0)
109+
{
110+
return _timeFormats[precision];
111+
}
112+
}
113+
114+
return _timeFormats[7];
115+
}
116+
}
117+
118+
/// <summary>
119+
/// Generates the SQL representation of a literal value without conversion.
120+
/// </summary>
121+
/// <param name="value">The literal value.</param>
122+
/// <returns>
123+
/// The generated string.
124+
/// </returns>
125+
protected override string GenerateNonNullSqlLiteral(object value)
126+
{
127+
return value is TimeSpan timeSpan && timeSpan.Milliseconds == 0
128+
? string.Format(CultureInfo.InvariantCulture, _timeFormats[0], value) //handle trailing decimal seperator when no fractional seconds
129+
: string.Format(CultureInfo.InvariantCulture, SqlLiteralFormatString, value);
64130
}
65131
}
66132
}

src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,8 @@ public SqlServerTypeMappingSource(
352352
"datetime2",
353353
"datetimeoffset",
354354
"double precision",
355-
"float"
355+
"float",
356+
"time"
356357
};
357358

358359
/// <summary>

test/EFCore.Relational.Tests/Storage/RelationalTypeMappingTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ public virtual void String_literal_generated_correctly()
529529
[ConditionalFact]
530530
public virtual void Timespan_literal_generated_correctly()
531531
{
532-
Test_GenerateSqlLiteral_helper(new TimeSpanTypeMapping("time"), new TimeSpan(7, 14, 30), "'07:14:30'");
532+
Test_GenerateSqlLiteral_helper(new TimeSpanTypeMapping("time"), new TimeSpan(0, 7, 14, 30, 123), "'07:14:30.1230000'");
533533
}
534534

535535
[ConditionalFact]

test/EFCore.SqlServer.FunctionalTests/BuiltInDataTypesSqlServerTest.cs

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,15 +1584,16 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_with_scale()
15841584
var parameters = DumpParameters();
15851585
Assert.Equal(
15861586
@"@p0='77'
1587-
@p1='2017-01-02T12:11:12.3210000' (Precision = 3)
1588-
@p2='2016-01-02T11:11:12.7650000+00:00' (Precision = 3)
1587+
@p1='2017-01-02T12:11:12.3210000' (Scale = 3)
1588+
@p2='2016-01-02T11:11:12.7650000+00:00' (Scale = 3)
15891589
@p3='102' (Precision = 3)
15901590
@p4='101' (Precision = 3)
15911591
@p5='103' (Precision = 3)
15921592
@p6='85.55000305175781' (Size = 25)
15931593
@p7='85.5' (Size = 3)
15941594
@p8='83.33000183105469' (Size = 25)
1595-
@p9='83.3' (Size = 3)",
1595+
@p9='83.3' (Size = 3)
1596+
@p10='12:34:56.7890123' (Scale = 3)",
15961597
parameters,
15971598
ignoreLineEndingDifferences: true);
15981599

@@ -1612,6 +1613,7 @@ private static void AssertMappedScaledDataTypes(MappedScaledDataTypes entity, in
16121613
Assert.Equal(
16131614
new DateTimeOffset(new DateTime(2016, 1, 2, 11, 11, 12, 765), TimeSpan.Zero), entity.DateTimeOffsetAsDatetimeoffset3);
16141615
Assert.Equal(new DateTime(2017, 1, 2, 12, 11, 12, 321), entity.DateTimeAsDatetime23);
1616+
Assert.Equal(TimeSpan.Parse("12:34:56.789", System.Globalization.CultureInfo.InvariantCulture), entity.TimeSpanAsTime3);
16151617
Assert.Equal(101m, entity.DecimalAsDecimal3);
16161618
Assert.Equal(102m, entity.DecimalAsDec3);
16171619
Assert.Equal(103m, entity.DecimalAsNumeric3);
@@ -1629,7 +1631,8 @@ private static MappedScaledDataTypes CreateMappedScaledDataTypes(int id)
16291631
DateTimeAsDatetime23 = new DateTime(2017, 1, 2, 12, 11, 12, 321),
16301632
DecimalAsDecimal3 = 101m,
16311633
DecimalAsDec3 = 102m,
1632-
DecimalAsNumeric3 = 103m
1634+
DecimalAsNumeric3 = 103m,
1635+
TimeSpanAsTime3 = TimeSpan.Parse("12:34:56.7890123", System.Globalization.CultureInfo.InvariantCulture)
16331636
};
16341637

16351638
[ConditionalFact]
@@ -1645,15 +1648,16 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_with_scale_se
16451648
var parameters = DumpParameters();
16461649
Assert.Equal(
16471650
@"@p0='77'
1648-
@p1='2017-01-02T12:11:12.3210000' (Precision = 3)
1649-
@p2='2016-01-02T11:11:12.7650000+00:00' (Precision = 3)
1651+
@p1='2017-01-02T12:11:12.3210000' (Scale = 3)
1652+
@p2='2016-01-02T11:11:12.7650000+00:00' (Scale = 3)
16501653
@p3='102' (Precision = 3)
16511654
@p4='101' (Precision = 3)
16521655
@p5='103' (Precision = 3)
16531656
@p6='85.55000305175781' (Size = 25)
16541657
@p7='85.5' (Size = 3)
16551658
@p8='83.33000183105469' (Size = 25)
1656-
@p9='83.3' (Size = 3)",
1659+
@p9='83.3' (Size = 3)
1660+
@p10='12:34:56.7890000' (Scale = 3)",
16571661
parameters,
16581662
ignoreLineEndingDifferences: true);
16591663

@@ -1676,6 +1680,7 @@ private static void AssertMappedScaledSeparatelyDataTypes(MappedScaledSeparately
16761680
Assert.Equal(101m, entity.DecimalAsDecimal3);
16771681
Assert.Equal(102m, entity.DecimalAsDec3);
16781682
Assert.Equal(103m, entity.DecimalAsNumeric3);
1683+
Assert.Equal(TimeSpan.Parse("12:34:56.789", System.Globalization.CultureInfo.InvariantCulture), entity.TimeSpanAsTime3);
16791684
}
16801685

16811686
private static MappedScaledSeparatelyDataTypes CreateMappedScaledSeparatelyDataTypes(int id)
@@ -1690,7 +1695,8 @@ private static MappedScaledSeparatelyDataTypes CreateMappedScaledSeparatelyDataT
16901695
DateTimeAsDatetime23 = new DateTime(2017, 1, 2, 12, 11, 12, 321),
16911696
DecimalAsDecimal3 = 101m,
16921697
DecimalAsDec3 = 102m,
1693-
DecimalAsNumeric3 = 103m
1698+
DecimalAsNumeric3 = 103m,
1699+
TimeSpanAsTime3 = TimeSpan.Parse("12:34:56.789", System.Globalization.CultureInfo.InvariantCulture)
16941700
};
16951701

16961702
[ConditionalFact]
@@ -2492,16 +2498,17 @@ public virtual void Can_insert_and_read_back_all_mapped_data_types_with_scale_wi
24922498

24932499
var parameters = DumpParameters();
24942500
Assert.Equal(
2495-
@"@p0='2017-01-02T12:11:12.1230000' (Precision = 3)
2496-
@p1='2016-01-02T11:11:12.5670000+00:00' (Precision = 3)
2501+
@"@p0='2017-01-02T12:11:12.1230000' (Scale = 3)
2502+
@p1='2016-01-02T11:11:12.5670000+00:00' (Scale = 3)
24972503
@p2='102' (Precision = 3)
24982504
@p3='101' (Precision = 3)
24992505
@p4='103' (Precision = 3)
25002506
@p5='85.55000305175781' (Size = 25)
25012507
@p6='85.5' (Size = 3)
25022508
@p7='83.33000183105469' (Size = 25)
25032509
@p8='83.3' (Size = 3)
2504-
@p9='77'",
2510+
@p9='77'
2511+
@p10='12:34:56.7890123' (Scale = 3)",
25052512
parameters,
25062513
ignoreLineEndingDifferences: true);
25072514

@@ -2524,6 +2531,7 @@ private static void AssertMappedScaledDataTypesWithIdentity(MappedScaledDataType
25242531
Assert.Equal(101m, entity.DecimalAsDecimal3);
25252532
Assert.Equal(102m, entity.DecimalAsDec3);
25262533
Assert.Equal(103m, entity.DecimalAsNumeric3);
2534+
Assert.Equal(TimeSpan.Parse("12:34:56.789", System.Globalization.CultureInfo.InvariantCulture), entity.TimeSpanAsTime3);
25272535
}
25282536

25292537
private static MappedScaledDataTypesWithIdentity CreateMappedScaledDataTypesWithIdentity(int id)
@@ -2538,7 +2546,8 @@ private static MappedScaledDataTypesWithIdentity CreateMappedScaledDataTypesWith
25382546
DateTimeAsDatetime23 = new DateTime(2017, 1, 2, 12, 11, 12, 123),
25392547
DecimalAsDecimal3 = 101m,
25402548
DecimalAsDec3 = 102m,
2541-
DecimalAsNumeric3 = 103m
2549+
DecimalAsNumeric3 = 103m,
2550+
TimeSpanAsTime3 = TimeSpan.Parse("12:34:56.7890123", System.Globalization.CultureInfo.InvariantCulture)
25422551
};
25432552

25442553
[ConditionalFact]
@@ -3242,6 +3251,7 @@ public virtual void Columns_have_expected_data_types()
32423251
MappedScaledDataTypes.FloatAsFloat25 ---> [float] [Precision = 53]
32433252
MappedScaledDataTypes.FloatAsFloat3 ---> [real] [Precision = 24]
32443253
MappedScaledDataTypes.Id ---> [int] [Precision = 10 Scale = 0]
3254+
MappedScaledDataTypes.TimeSpanAsTime3 ---> [time] [Precision = 3]
32453255
MappedScaledDataTypesWithIdentity.DateTimeAsDatetime23 ---> [datetime2] [Precision = 3]
32463256
MappedScaledDataTypesWithIdentity.DateTimeOffsetAsDatetimeoffset3 ---> [datetimeoffset] [Precision = 3]
32473257
MappedScaledDataTypesWithIdentity.DecimalAsDec3 ---> [decimal] [Precision = 3 Scale = 0]
@@ -3253,6 +3263,7 @@ public virtual void Columns_have_expected_data_types()
32533263
MappedScaledDataTypesWithIdentity.FloatAsFloat3 ---> [real] [Precision = 24]
32543264
MappedScaledDataTypesWithIdentity.Id ---> [int] [Precision = 10 Scale = 0]
32553265
MappedScaledDataTypesWithIdentity.Int ---> [int] [Precision = 10 Scale = 0]
3266+
MappedScaledDataTypesWithIdentity.TimeSpanAsTime3 ---> [time] [Precision = 3]
32563267
MappedScaledSeparatelyDataTypes.DateTimeAsDatetime23 ---> [datetime2] [Precision = 3]
32573268
MappedScaledSeparatelyDataTypes.DateTimeOffsetAsDatetimeoffset3 ---> [datetimeoffset] [Precision = 3]
32583269
MappedScaledSeparatelyDataTypes.DecimalAsDec3 ---> [decimal] [Precision = 3 Scale = 0]
@@ -3263,6 +3274,7 @@ public virtual void Columns_have_expected_data_types()
32633274
MappedScaledSeparatelyDataTypes.FloatAsFloat25 ---> [float] [Precision = 53]
32643275
MappedScaledSeparatelyDataTypes.FloatAsFloat3 ---> [real] [Precision = 24]
32653276
MappedScaledSeparatelyDataTypes.Id ---> [int] [Precision = 10 Scale = 0]
3277+
MappedScaledSeparatelyDataTypes.TimeSpanAsTime3 ---> [time] [Precision = 3]
32663278
MappedSizedDataTypes.BytesAsBinary3 ---> [nullable binary] [MaxLength = 3]
32673279
MappedSizedDataTypes.BytesAsBinaryVarying3 ---> [nullable varbinary] [MaxLength = 3]
32683280
MappedSizedDataTypes.BytesAsVarbinary3 ---> [nullable varbinary] [MaxLength = 3]
@@ -4137,6 +4149,9 @@ protected class MappedScaledDataTypes
41374149

41384150
[Column(TypeName = "numeric(3)")]
41394151
public decimal DecimalAsNumeric3 { get; set; }
4152+
4153+
[Column(TypeName = "time(3)")]
4154+
public TimeSpan TimeSpanAsTime3 { get; set; }
41404155
}
41414156

41424157
protected class MappedScaledSeparatelyDataTypes
@@ -4169,6 +4184,9 @@ protected class MappedScaledSeparatelyDataTypes
41694184

41704185
[Column(TypeName = "numeric")]
41714186
public decimal DecimalAsNumeric3 { get; set; }
4187+
4188+
[Column(TypeName = "time(3)")]
4189+
public TimeSpan TimeSpanAsTime3 { get; set; }
41724190
}
41734191

41744192
protected class DoubleDataTypes
@@ -4621,6 +4639,9 @@ protected class MappedScaledDataTypesWithIdentity
46214639

46224640
[Column(TypeName = "numeric(3)")]
46234641
public decimal DecimalAsNumeric3 { get; set; }
4642+
4643+
[Column(TypeName = "time(3)")]
4644+
public TimeSpan TimeSpanAsTime3 { get; set; }
46244645
}
46254646

46264647
protected class MappedPrecisionAndScaledDataTypesWithIdentity

0 commit comments

Comments
 (0)