mirror of
https://github.com/DapperLib/Dapper.git
synced 2026-10-20 07:38:19 +08:00
6d48ef664a
DateOnly/TimeOnly support (#2051) was compiled out in 907a4d9 pending
#2071/#2072. This re-enables it with the root causes fixed, and teaches the
read paths that the date/time family has no IConvertible bridge - which box
a date or time column yields is a provider/version decision (Npgsql 10:
DateOnly/TimeOnly; SqlClient and Npgsql 9: DateTime/TimeSpan), so both
shapes must convert:
- GetFieldValue<T> is no longer demanded of a column whose reported type
needs a family conversion (a datetime column into a DateOnly member was
the #2072 breakage; SqlDecimal-style entries, where GetFieldValue from a
differently-reported column is the whole point, keep it);
- the member, scalar and Parse<T> paths all convert DateOnly/TimeOnly <->
DateTime/TimeSpan in both directions (IL for members, object-level
elsewhere), fixing reads against Npgsql 10 date/time columns (#2226);
- Query<DateOnly>/<TimeOnly> in scalar form no longer silently return
default(T) (#2227): the typeMap entries make them simple types again;
- the same-TypeCode direct-unbox shortcut is restricted to distinct codes:
TypeCode.Object matching TypeCode.Object says nothing (TimeSpan into
TimeOnly? threw), while object on either side (sql_variant columns,
dynamic members) keeps the direct unbox;
- the scalar conversion fallback keeps Convert.ChangeType's strict null
contract (TestConversionExceptionMessages pins it).
The parked DateTimeOnlyTests are re-enabled, with new tests for the #2072
shape (datetime column into DateOnly member), the #2227 scalar shape, and
date-as-DateTime regression guards; PostgresqlTests gains the Npgsql-10
interchange matrix ([FactPostgresql], so it runs where the documented
container is available).
Fixes #2072, #2226, #2227, #1728; expected to also resolve #2071.
118 lines
4.8 KiB
C#
118 lines
4.8 KiB
C#
using System;
|
|
using System.Threading.Tasks;
|
|
using Xunit;
|
|
|
|
#if NET6_0_OR_GREATER
|
|
namespace Dapper.Tests;
|
|
|
|
/* we do **NOT** expect this to work against System.Data
|
|
[Collection("DateTimeOnlyTests")]
|
|
public sealed class SystemSqlClientDateTimeOnlyTests : DateTimeOnlyTests<SystemSqlClientProvider> { }
|
|
*/
|
|
#if MSSQLCLIENT
|
|
[Collection("DateTimeOnlyTests")]
|
|
public sealed class MicrosoftSqlClientDateTimeOnlyTests : DateTimeOnlyTests<MicrosoftSqlClientProvider> { }
|
|
#endif
|
|
public abstract class DateTimeOnlyTests<TProvider> : TestBase<TProvider> where TProvider : DatabaseProvider
|
|
{
|
|
public class HazDateTimeOnly
|
|
{
|
|
public string Name { get; set; } = "";
|
|
public DateOnly Date { get; set; }
|
|
public TimeOnly Time { get; set; }
|
|
public DateOnly? NDate { get; set; }
|
|
public TimeOnly? NTime { get; set; }
|
|
}
|
|
|
|
[Fact]
|
|
public void TypedInOut()
|
|
{
|
|
var now = DateTime.Now;
|
|
var args = new HazDateTimeOnly
|
|
{
|
|
Name = nameof(TypedInOut),
|
|
Date = DateOnly.FromDateTime(now),
|
|
Time = TimeOnly.FromDateTime(now),
|
|
NDate = DateOnly.FromDateTime(now),
|
|
NTime = TimeOnly.FromDateTime(now),
|
|
};
|
|
var row = connection.QuerySingle<HazDateTimeOnly>("select @name as [Name], @date as [Date], @time as [Time], @ndate as [NDate], @ntime as [NTime]", args);
|
|
Assert.Equal(args.Name, row.Name);
|
|
Assert.Equal(args.Date, row.Date);
|
|
Assert.Equal(args.Time, row.Time);
|
|
Assert.Equal(args.NDate, row.NDate);
|
|
Assert.Equal(args.NTime, row.NTime);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task TypedInOutAsync()
|
|
{
|
|
var now = DateTime.Now;
|
|
var args = new HazDateTimeOnly
|
|
{
|
|
Name = nameof(TypedInOutAsync),
|
|
Date = DateOnly.FromDateTime(now),
|
|
Time = TimeOnly.FromDateTime(now),
|
|
NDate = DateOnly.FromDateTime(now),
|
|
NTime = TimeOnly.FromDateTime(now),
|
|
};
|
|
var row = await connection.QuerySingleAsync<HazDateTimeOnly>("select @name as [Name], @date as [Date], @time as [Time], @ndate as [NDate], @ntime as [NTime]", args);
|
|
Assert.Equal(args.Name, row.Name);
|
|
Assert.Equal(args.Date, row.Date);
|
|
Assert.Equal(args.Time, row.Time);
|
|
Assert.Equal(args.NDate, row.NDate);
|
|
Assert.Equal(args.NTime, row.NTime);
|
|
}
|
|
|
|
[Fact]
|
|
public void UntypedInOut()
|
|
{
|
|
var now = DateTime.Now;
|
|
var args = new DynamicParameters();
|
|
var name = nameof(UntypedInOut);
|
|
var date = DateOnly.FromDateTime(now);
|
|
var time = TimeOnly.FromDateTime(now);
|
|
args.Add("name", name);
|
|
args.Add("date", date);
|
|
args.Add("time", time);
|
|
var row = connection.QuerySingle<dynamic>("select @name as [Name], @date as [Date], @time as [Time]", args);
|
|
Assert.Equal(name, (string)row.Name);
|
|
// untyped, observation is that these come back as DateTime and TimeSpan
|
|
Assert.Equal(date, DateOnly.FromDateTime((DateTime)row.Date));
|
|
Assert.Equal(time, TimeOnly.FromTimeSpan((TimeSpan)row.Time));
|
|
}
|
|
|
|
[Fact] // #2072: a datetime column into a DateOnly member - the provider boxes DateTime,
|
|
// so this must convert rather than demand GetFieldValue<DateOnly> of a datetime column
|
|
public void MembersFromDateTimeAndTimeSpanColumns()
|
|
{
|
|
var row = connection.QuerySingle<HazDateTimeOnly>(
|
|
"select 'x' as [Name], cast('2019-10-01' as datetime) as [Date], cast('03:03:03' as time) as [Time], cast('2019-10-02' as datetime) as [NDate], cast('04:04:04' as time) as [NTime]");
|
|
Assert.Equal(new DateOnly(2019, 10, 1), row.Date);
|
|
Assert.Equal(new TimeOnly(3, 3, 3), row.Time);
|
|
Assert.Equal(new DateOnly(2019, 10, 2), row.NDate);
|
|
Assert.Equal(new TimeOnly(4, 4, 4), row.NTime);
|
|
}
|
|
|
|
[Fact] // #2227: the scalar form must not silently yield default(T)
|
|
public void ScalarDateOnlyAndTimeOnly()
|
|
{
|
|
Assert.Equal(new DateOnly(2021, 1, 1), connection.QuerySingle<DateOnly>("select cast('2021-01-01' as date)"));
|
|
Assert.Equal(new TimeOnly(3, 3, 3), connection.QuerySingle<TimeOnly>("select cast('03:03:03' as time)"));
|
|
Assert.Equal(new DateOnly(2021, 1, 1), connection.QuerySingle<DateOnly?>("select cast('2021-01-01' as date)"));
|
|
}
|
|
|
|
[Fact] // the pre-DateOnly reading of a date column must keep working
|
|
public void DateColumnAsDateTime()
|
|
{
|
|
Assert.Equal(new DateTime(2021, 1, 1), connection.QuerySingle<DateTime>("select cast('2021-01-01' as date)"));
|
|
Assert.Equal(new DateTime(2021, 1, 1), connection.QuerySingle<HazDateTime>("select cast('2021-01-01' as date) as [When]").When);
|
|
}
|
|
|
|
public class HazDateTime
|
|
{
|
|
public DateTime When { get; set; }
|
|
}
|
|
}
|
|
#endif
|