Skip to content

Commit d162ec9

Browse files
authored
feat: DateOnly and TimeOnly support (#129)
* init * investigate PostgreSql expected data read * run tests for MSSQL as well (different types) * integration tests work as well! * and more integration tests * and filter tests which should not run * dont change indents * spaces or tabs? * add description of how Integration tests work * fix build warnings
1 parent 68c6b93 commit d162ec9

File tree

20 files changed

+752
-9
lines changed

20 files changed

+752
-9
lines changed

Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
<PackageVersion Include="System.Data.Common" Version="4.3.0" />
3434
<PackageVersion Include="System.Data.SqlClient" Version="4.8.5" />
3535
<PackageVersion Include="System.Memory" Version="4.5.5" />
36+
<PackageVersion Include="Testcontainers.MsSql" Version="3.10.0" />
3637
<PackageVersion Include="Testcontainers.PostgreSql" Version="3.6.0" />
3738
<!-- these are bound by Microsoft.CodeAnalysis.CSharp.*.Testing.XUnit -->
3839
<PackageVersion Include="xunit" Version="[2.3.0]" />

src/Dapper.AOT/Internal/CommandUtils.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,46 @@ internal static T As<T>(object? value)
165165
DateTime? t = Convert.ToDateTime(value, CultureInfo.InvariantCulture);
166166
return Unsafe.As<DateTime?, T>(ref t);
167167
}
168+
#if NET6_0_OR_GREATER
169+
else if (typeof(T) == typeof(DateOnly))
170+
{
171+
if (value is DateOnly only) return Unsafe.As<DateOnly, T>(ref only);
172+
173+
DateTime t = Convert.ToDateTime(value, CultureInfo.InvariantCulture);
174+
var dateOnly = DateOnly.FromDateTime(t);
175+
return Unsafe.As<DateOnly, T>(ref dateOnly);
176+
}
177+
else if (typeof(T) == typeof(DateOnly?))
178+
{
179+
DateTime? t = Convert.ToDateTime(value, CultureInfo.InvariantCulture);
180+
DateOnly? dateOnly = t is null ? null : DateOnly.FromDateTime(t.Value);
181+
return Unsafe.As<DateOnly?, T>(ref dateOnly);
182+
}
183+
else if (typeof(T) == typeof(TimeOnly))
184+
{
185+
if (value is TimeSpan timeSpan)
186+
{
187+
var fromSpan = TimeOnly.FromTimeSpan(timeSpan);
188+
return Unsafe.As<TimeOnly, T>(ref fromSpan);
189+
}
190+
191+
DateTime t = Convert.ToDateTime(value, CultureInfo.InvariantCulture);
192+
var timeOnly = TimeOnly.FromDateTime(t);
193+
return Unsafe.As<TimeOnly, T>(ref timeOnly);
194+
}
195+
else if (typeof(T) == typeof(TimeOnly?))
196+
{
197+
if (value is TimeSpan timeSpan)
198+
{
199+
var fromSpan = TimeOnly.FromTimeSpan(timeSpan);
200+
return Unsafe.As<TimeOnly, T>(ref fromSpan);
201+
}
202+
203+
DateTime? t = Convert.ToDateTime(value, CultureInfo.InvariantCulture);
204+
TimeOnly? timeOnly = t is null ? null : TimeOnly.FromDateTime(t.Value);
205+
return Unsafe.As<TimeOnly?, T>(ref timeOnly);
206+
}
207+
#endif
168208
else if (typeof(T) == typeof(Guid) && (s = value as string) is not null)
169209
{
170210
Guid t = Guid.Parse(s);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
using System.Runtime.CompilerServices;
2+
3+
[assembly: InternalsVisibleTo("Dapper.AOT.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100a17ba361da0990b3da23f3c20f2a002242397b452a28f27832d61d49f35edb54a68b98d98557b8a02be79be42142339c7861af309c8917dee972775e2c358dd6b96109a9147987652b25b8dc52e7f61f22a755831674f0a3cea17bef9abb6b23ef1856a02216864a1ffbb04a4c549258d32ba740fe141dad2f298a8130ea56d0")]

test/Dapper.AOT.Test.Integration.Executables/Dapper.AOT.Test.Integration.Executables.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<TargetFrameworks>net8.0;net6.0;net48</TargetFrameworks>
3+
<TargetFrameworks>net8.0;net6.0</TargetFrameworks>
44
<RootNamespace>Dapper.AOT.Test.Integration.Executables</RootNamespace>
55
</PropertyGroup>
66

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
3+
namespace Dapper.AOT.Test.Integration.Executables.Models;
4+
5+
public class DateOnlyTimeOnlyPoco
6+
{
7+
public const string TableName = "dateOnlyTimeOnly";
8+
9+
public static DateOnly SpecificDate => DateOnly.FromDateTime(new DateTime(year: 2022, month: 2, day: 2));
10+
public static TimeOnly SpecificTime => TimeOnly.FromDateTime(new DateTime(year: 2022, month: 1, day: 1, hour: 10, minute: 11, second: 12));
11+
12+
public int Id { get; set; }
13+
public DateOnly Date { get; set; }
14+
public TimeOnly Time { get; set; }
15+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System.Data;
2+
using System.Linq;
3+
using Dapper.AOT.Test.Integration.Executables.Models;
4+
5+
namespace Dapper.AOT.Test.Integration.Executables.UserCode.DateOnlyTimeOnly;
6+
7+
[DapperAot]
8+
public class DateOnlyInsert : IExecutable<DateOnlyTimeOnlyPoco>
9+
{
10+
public DateOnlyTimeOnlyPoco Execute(IDbConnection connection)
11+
{
12+
connection.Execute(
13+
$"""
14+
insert into {DateOnlyTimeOnlyPoco.TableName}(id, date, time)
15+
values (@id, @date, @time)
16+
on conflict (id) do nothing;
17+
""",
18+
new DateOnlyTimeOnlyPoco { Id = 2, Date = DateOnlyTimeOnlyPoco.SpecificDate, Time = DateOnlyTimeOnlyPoco.SpecificTime }
19+
);
20+
21+
var results = connection.Query<DateOnlyTimeOnlyPoco>(
22+
$"""
23+
select * from {DateOnlyTimeOnlyPoco.TableName}
24+
where id = 2
25+
"""
26+
);
27+
28+
return results.First();
29+
}
30+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using System.Data;
3+
using System.Linq;
4+
using Dapper.AOT.Test.Integration.Executables.Models;
5+
6+
namespace Dapper.AOT.Test.Integration.Executables.UserCode.DateOnlyTimeOnly;
7+
8+
[DapperAot]
9+
public class DateOnlyTimeOnlyUsage : IExecutable<DateOnlyTimeOnlyPoco>
10+
{
11+
public DateOnlyTimeOnlyPoco Execute(IDbConnection connection)
12+
{
13+
var results = connection.Query<DateOnlyTimeOnlyPoco>($"select * from {DateOnlyTimeOnlyPoco.TableName}");
14+
return results.First();
15+
}
16+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
using System.Data;
3+
using System.Linq;
4+
using Dapper.AOT.Test.Integration.Executables.Models;
5+
6+
namespace Dapper.AOT.Test.Integration.Executables.UserCode.DateOnlyTimeOnly;
7+
8+
[DapperAot]
9+
public class DateOnlyUsageWithDateFilter : IExecutable<DateOnlyTimeOnlyPoco>
10+
{
11+
public DateOnlyTimeOnlyPoco Execute(IDbConnection connection)
12+
{
13+
var results = connection.Query<DateOnlyTimeOnlyPoco>(
14+
$"""
15+
select * from {DateOnlyTimeOnlyPoco.TableName}
16+
where date = @date
17+
""",
18+
new { date = DateOnlyTimeOnlyPoco.SpecificDate }
19+
);
20+
21+
return results.First();
22+
}
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
using System.Data;
3+
using System.Linq;
4+
using Dapper.AOT.Test.Integration.Executables.Models;
5+
6+
namespace Dapper.AOT.Test.Integration.Executables.UserCode.DateOnlyTimeOnly;
7+
8+
[DapperAot]
9+
public class DateOnlyUsageWithTimeFilter : IExecutable<DateOnlyTimeOnlyPoco>
10+
{
11+
public DateOnlyTimeOnlyPoco Execute(IDbConnection connection)
12+
{
13+
var results = connection.Query<DateOnlyTimeOnlyPoco>(
14+
$"""
15+
select * from {DateOnlyTimeOnlyPoco.TableName}
16+
where time = @time
17+
""",
18+
new { time = DateOnlyTimeOnlyPoco.SpecificTime }
19+
);
20+
21+
return results.First();
22+
}
23+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using System;
2+
using System.Data;
3+
using Dapper.AOT.Test.Integration.Executables.Models;
4+
using Dapper.AOT.Test.Integration.Executables.UserCode.DateOnlyTimeOnly;
5+
using Dapper.AOT.Test.Integration.Setup;
6+
7+
namespace Dapper.AOT.Test.Integration;
8+
9+
[Collection(SharedPostgresqlClient.Collection)]
10+
public class DateOnlyTimeOnlyTests : IntegrationTestsBase
11+
{
12+
public DateOnlyTimeOnlyTests(PostgresqlFixture fixture) : base(fixture)
13+
{
14+
}
15+
16+
protected override void SetupDatabase(IDbConnection dbConnection)
17+
{
18+
base.SetupDatabase(dbConnection);
19+
20+
dbConnection.Execute($"""
21+
CREATE TABLE IF NOT EXISTS {DateOnlyTimeOnlyPoco.TableName}(
22+
id integer PRIMARY KEY,
23+
date DATE,
24+
time TIME
25+
);
26+
27+
TRUNCATE {DateOnlyTimeOnlyPoco.TableName};
28+
29+
INSERT INTO {DateOnlyTimeOnlyPoco.TableName} (id, date, time)
30+
VALUES (1, '{DateOnlyTimeOnlyPoco.SpecificDate.ToString("yyyy-MM-dd")}', '{DateOnlyTimeOnlyPoco.SpecificTime.ToString("HH:mm:ss")}')
31+
""");
32+
}
33+
34+
[Fact]
35+
public void DateOnly_BasicUsage_InterceptsAndReturnsExpectedData()
36+
{
37+
var result = ExecuteInterceptedUserCode<DateOnlyTimeOnlyUsage, DateOnlyTimeOnlyPoco>(DbConnection);
38+
Assert.Equal(1, result.Id);
39+
Assert.Equal(DateOnlyTimeOnlyPoco.SpecificDate, result.Date);
40+
Assert.Equal(DateOnlyTimeOnlyPoco.SpecificTime, result.Time);
41+
}
42+
43+
[Fact]
44+
public void DateOnly_WithDateFilter_InterceptsAndReturnsExpectedData()
45+
{
46+
var result = ExecuteInterceptedUserCode<DateOnlyUsageWithDateFilter, DateOnlyTimeOnlyPoco>(DbConnection);
47+
Assert.Equal(1, result.Id);
48+
Assert.Equal(DateOnlyTimeOnlyPoco.SpecificDate, result.Date);
49+
Assert.Equal(DateOnlyTimeOnlyPoco.SpecificTime, result.Time);
50+
}
51+
52+
[Fact]
53+
public void DateOnly_WithTimeFilter_InterceptsAndReturnsExpectedData()
54+
{
55+
var result = ExecuteInterceptedUserCode<DateOnlyUsageWithTimeFilter, DateOnlyTimeOnlyPoco>(DbConnection);
56+
Assert.Equal(1, result.Id);
57+
Assert.Equal(DateOnlyTimeOnlyPoco.SpecificDate, result.Date);
58+
Assert.Equal(DateOnlyTimeOnlyPoco.SpecificTime, result.Time);
59+
}
60+
61+
[Fact]
62+
public void DateOnly_Inserts_InterceptsAndReturnsExpectedData()
63+
{
64+
var result = ExecuteInterceptedUserCode<DateOnlyInsert, DateOnlyTimeOnlyPoco>(DbConnection);
65+
Assert.Equal(2, result.Id);
66+
Assert.Equal(DateOnlyTimeOnlyPoco.SpecificDate, result.Date);
67+
Assert.Equal(DateOnlyTimeOnlyPoco.SpecificTime, result.Time);
68+
}
69+
}

0 commit comments

Comments
 (0)