From 99f1da4f45529233248c09f74482076145f7075f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Mon, 18 May 2026 21:16:30 +0200 Subject: [PATCH 1/2] Map `DbType.Date` to `DateOnly` on targets where it's available --- source/Sylvan.Data.Tests/DataBinderTests.cs | 35 ++++++++++++--------- source/Sylvan.Data/DataBinderAccessors.cs | 5 +++ 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/source/Sylvan.Data.Tests/DataBinderTests.cs b/source/Sylvan.Data.Tests/DataBinderTests.cs index 33f11cf0..01660323 100644 --- a/source/Sylvan.Data.Tests/DataBinderTests.cs +++ b/source/Sylvan.Data.Tests/DataBinderTests.cs @@ -1,13 +1,18 @@ using Sylvan.Data.Csv; using System; using System.Collections.ObjectModel; -using System.Data; using System.Data.Common; using System.IO; using System.Linq; using System.Runtime.Serialization; using Xunit; +#if NET6_0_OR_GREATER +using DateType = System.DateOnly; +#else +using DateType = System.DateTime; +#endif + namespace Sylvan.Data { public class DataBinderTests @@ -18,7 +23,7 @@ static ReadOnlyCollection BuildSchema() new Schema.Builder() .Add("Id") .Add("Name") - .Add("Date") + .Add("Date") .Build(); return schema.GetColumnSchema(); } @@ -100,7 +105,7 @@ public void RecordClass() { Assert.Equal(1, record.Id); Assert.Equal("Test", record.Name); - Assert.Equal(new DateTime(2020, 8, 12), record.Date); + Assert.Equal(new DateType(2020, 8, 12), record.Date); } } @@ -332,7 +337,7 @@ class PopulationRecord { public string State { get; set; } public string County { get; set; } - public Series Values { get; set; } + public Series Values { get; set; } } [Fact] @@ -404,7 +409,7 @@ public void Manual() sealed class ManualBinder : IDataBinder { - readonly DataSeriesAccessor series0; + readonly DataSeriesAccessor series0; readonly int idIdx; readonly int nameIdx; @@ -414,16 +419,16 @@ public ManualBinder(ReadOnlyCollection schema) nameIdx = schema.Single(c => c.ColumnName == "Name").ColumnOrdinal.Value; var seriesCols = schema - .Where(c => DateTime.TryParse(c.ColumnName, out _)) - .Select(c => new DataSeriesColumn(c.ColumnName, DateTime.Parse(c.ColumnName), c.ColumnOrdinal.Value)); - this.series0 = new DataSeriesAccessor(seriesCols); + .Where(c => DateType.TryParse(c.ColumnName, out _)) + .Select(c => new DataSeriesColumn(c.ColumnName, DateType.Parse(c.ColumnName), c.ColumnOrdinal.Value)); + this.series0 = new DataSeriesAccessor(seriesCols); } public void Bind(DbDataReader record, SeriesDateRecord item) { item.Id = record.GetInt32(idIdx); item.Name = record.GetString(nameIdx); - item.Values = new Series(this.series0, record); + item.Values = new Series(this.series0, record); } public void Bind(DbDataReader record, object item) @@ -480,7 +485,7 @@ public void BindErrorTest() new Schema.Builder() .Add() .Add() - .Add() + .Add() .Build(); var data = CsvDataReader.Create(new StringReader(dataStr), new CsvDataReaderOptions { Schema = new CsvSchema(schema) }); @@ -501,7 +506,7 @@ public void DupeHeaderSuccess() .Add("Id") .Add("Blorp") .Add("Name") - .Add("Date") + .Add("Date") .Add("Blorp") .Build(); @@ -524,7 +529,7 @@ public void DupeHeaderFailure() new Schema.Builder() .Add("Id") .Add("Name") - .Add("Date") + .Add("Date") .Add("Name") .Build(); @@ -571,7 +576,7 @@ class MyDataRecord { public int Id { get; private set; } public string Name { get; private set; } - public DateTime? Date { get; private set; } + public DateType? Date { get; private set; } } class NumericNullRecord @@ -600,7 +605,7 @@ class SeriesDateRecord { public int Id { get; set; } public string Name { get; set; } - public Series Values { get; set; } + public Series Values { get; set; } } class Record @@ -637,7 +642,7 @@ record class MyRecordClass { public int Id { get; set; } public string Name { get; set; } - public DateTime Date { get; set; } + public DateType Date { get; set; } } #if NET6_0_OR_GREATER diff --git a/source/Sylvan.Data/DataBinderAccessors.cs b/source/Sylvan.Data/DataBinderAccessors.cs index 4b95d7cb..e9868f81 100644 --- a/source/Sylvan.Data/DataBinderAccessors.cs +++ b/source/Sylvan.Data/DataBinderAccessors.cs @@ -306,8 +306,13 @@ internal static Type GetDataType(DbType type) return typeof(Guid); case DbType.DateTime: case DbType.DateTime2: + return typeof(DateTime); case DbType.Date: +#if NET6_0_OR_GREATER + return typeof(DateOnly); +#else return typeof(DateTime); +#endif case DbType.DateTimeOffset: return typeof(DateTimeOffset); } From 90b4a1851c26335192947db62ac86d86c871a619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Tue, 14 Apr 2026 14:11:26 +0200 Subject: [PATCH 2/2] Introduce an AppContext switch to restore mapping DbType.Date to DateTime The behaviour of mapping mapping DbType.Date to DateTime (instead of DateOnly) can be restored by setting the new `Sylvan.Data.MapDbDateToClrDateTime` AppContext switch. ```csharp AppContext.SetSwitch("Sylvan.Data.MapDbDateToClrDateTime", true); ``` --- source/Sylvan.Data/DataBinderAccessors.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/Sylvan.Data/DataBinderAccessors.cs b/source/Sylvan.Data/DataBinderAccessors.cs index e9868f81..621010de 100644 --- a/source/Sylvan.Data/DataBinderAccessors.cs +++ b/source/Sylvan.Data/DataBinderAccessors.cs @@ -27,6 +27,8 @@ partial class DataBinder return name; } + private static readonly bool MapDbDateToClrDateTime; + //internal static readonly Type IDataRecordType = typeof(IDataRecord); internal static readonly Type DbDataRecordType = typeof(DbDataRecord); @@ -52,6 +54,8 @@ partial class DataBinder #endif static DataBinder() { + AppContext.TryGetSwitch("Sylvan.Data.MapDbDateToClrDateTime", out MapDbDateToClrDateTime); + //IDataRecordType = typeof(IDataRecord); DbDataRecordType = typeof(DbDataReader); IsDbNullMethod = DbDataRecordType.GetMethod("IsDBNull")!; @@ -309,7 +313,7 @@ internal static Type GetDataType(DbType type) return typeof(DateTime); case DbType.Date: #if NET6_0_OR_GREATER - return typeof(DateOnly); + return MapDbDateToClrDateTime ? typeof(DateTime) : typeof(DateOnly); #else return typeof(DateTime); #endif