Skip to content

Commit 5270022

Browse files
Support DateOnly / TimeOnly (#613)
1 parent 537b461 commit 5270022

15 files changed

Lines changed: 186 additions & 18 deletions

File tree

net/DevExtreme.AspNet.Data.Tests.Common/RemoteGroupingStressHelper.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4-
using System.Text;
54
using Xunit;
65

76
namespace DevExtreme.AspNet.Data.Tests {
@@ -11,12 +10,18 @@ public static class RemoteGroupingStressHelper {
1110
const string PROP_NULL_NUM = nameof(IEntity.NullNum);
1211
const string PROP_DATE = nameof(IEntity.Date);
1312
const string PROP_NULL_DATE = nameof(IEntity.NullDate);
13+
#if EFCORE8 || EFCORE9
14+
const string PROP_DATE_ONLY = nameof(IEntity.DateO);
15+
#endif
1416

1517
public interface IEntity {
1618
int Num { get; }
1719
int? NullNum { get; }
1820
DateTime Date { get; }
1921
DateTime? NullDate { get; }
22+
#if EFCORE8 || EFCORE9
23+
DateOnly DateO { get; set; }
24+
#endif
2025
}
2126

2227
public static void Run<T>(IQueryable<T> data) where T : IEntity {
@@ -36,7 +41,10 @@ static GroupingInfo[] BuildGroupParams() {
3641
new GroupingInfo { Selector = PROP_NUM },
3742
new GroupingInfo { Selector = PROP_NULL_NUM },
3843
new GroupingInfo { Selector = PROP_DATE },
39-
new GroupingInfo { Selector = PROP_NULL_DATE }
44+
new GroupingInfo { Selector = PROP_NULL_DATE },
45+
#if EFCORE8 || EFCORE9
46+
new GroupingInfo { Selector = PROP_DATE_ONLY },
47+
#endif
4048
};
4149

4250
foreach(var interval in Enumerable.Range(1, 3).Select(i => (100 * i).ToString())) {

net/DevExtreme.AspNet.Data.Tests.EFCore/RemoteGroupingStress.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ public class DataItem : RemoteGroupingStressHelper.IEntity {
1515
public int? NullNum { get; set; }
1616
public DateTime Date { get; set; }
1717
public DateTime? NullDate { get; set; }
18+
#if EFCORE8 || EFCORE9
19+
public DateOnly DateO { get; set; }
20+
#endif
1821
}
1922

2023
[Fact]

net/DevExtreme.AspNet.Data.Tests.NH/RemoteGroupingStress.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ public class DataItem : RemoteGroupingStressHelper.IEntity {
1313
public virtual int? NullNum { get; set; }
1414
public virtual DateTime Date { get; set; }
1515
public virtual DateTime? NullDate { get; set; }
16+
//#if EFCORE8 || EFCORE9
17+
// dummy interface implementation
18+
public virtual DateOnly DateO { get; set; }
19+
//#endif
1620
}
1721

1822
public class DataItemMap : ClassMap<DataItem> {
@@ -23,10 +27,11 @@ public DataItemMap() {
2327
Map(i => i.NullNum);
2428
Map(i => i.Date);
2529
Map(i => i.NullDate);
30+
//Map(i => i.DateO); //used by all fixtures, requires nh feature support (see skip)
2631
}
2732
}
2833

29-
[Fact]
34+
[Fact(Skip = "Skip until https://github.com/nhibernate/nhibernate-core/issues/2912 is implemented?")]
3035
public async Task Scenario() {
3136
await SessionFactoryHelper.ExecAsync(session => {
3237
session.Save(new DataItem());

net/DevExtreme.AspNet.Data.Tests.Xpo/DevExtreme.AspNet.Data.Tests.Xpo.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFramework>net6.0</TargetFramework>
55
</PropertyGroup>
66

77
<ItemGroup>
8-
<PackageReference Include="DevExpress.Xpo" Version="22.2.3" />
8+
<PackageReference Include="DevExpress.Xpo" Version="23.2.5" />
99
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
1010
<PackageReference Include="xunit" Version="2.4.2" />
1111
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" />

net/DevExtreme.AspNet.Data.Tests.Xpo/RemoteGroupingStress.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ public class DataItem : XPLiteObject, RemoteGroupingStressHelper.IEntity {
1414
int? _nullNum;
1515
DateTime _date;
1616
DateTime? _nullDate;
17+
//#if EFCORE8 || EFCORE9
18+
DateOnly _dateO;
19+
//#endif
1720

1821
public DataItem(Session session)
1922
: base(session) {
@@ -45,9 +48,15 @@ public DateTime? NullDate {
4548
set { SetPropertyValue(nameof(NullDate), ref _nullDate, value); }
4649
}
4750

51+
//#if EFCORE8 || EFCORE9
52+
public DateOnly DateO {
53+
get { return _dateO; }
54+
set { SetPropertyValue(nameof(DateO), ref _dateO, value); }
55+
}
56+
//#endif
4857
}
4958

50-
[Fact]
59+
[Fact(Skip = "Skip until proper DevExpress.Xpo dll / nupkg with Date Time Only support?")]
5160
public async Task Scenario() {
5261
await UnitOfWorkHelper.ExecAsync(uow => {
5362
new DataItem(uow);

net/DevExtreme.AspNet.Data/GroupHelper.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
using DevExtreme.AspNet.Data.Helpers;
22
using DevExtreme.AspNet.Data.ResponseModel;
3+
34
using System;
45
using System.Collections.Generic;
56
using System.Linq;
6-
using System.Linq.Expressions;
7-
using System.Reflection;
8-
using System.Threading.Tasks;
97

108
namespace DevExtreme.AspNet.Data {
119

@@ -96,6 +94,11 @@ static DateTime ToDateTime(object value) {
9694
if(value is DateTimeOffset offset)
9795
return offset.DateTime;
9896

97+
#if NET6_0_OR_GREATER
98+
if(value is DateOnly date)
99+
return date.ToDateTime(TimeOnly.MinValue);
100+
#endif
101+
99102
return Convert.ToDateTime(value);
100103
}
101104
}

net/DevExtreme.AspNet.Data/Utils.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ public static object ConvertClientValue(object value, Type type) {
4848
if(type == typeof(DateTimeOffset) && value is DateTime date)
4949
return new DateTimeOffset(date);
5050

51+
#if NET6_0_OR_GREATER
52+
if(type == typeof(DateOnly) && value is String)
53+
return DateOnly.Parse((string)value, CultureInfo.InvariantCulture);
54+
if(type == typeof(TimeOnly) && value is String)
55+
return TimeOnly.Parse((string)value, CultureInfo.InvariantCulture);
56+
#endif
57+
5158
var converter = TypeDescriptor.GetConverter(type);
5259
if(converter != null && converter.CanConvertFrom(value.GetType()))
5360
return converter.ConvertFrom(null, CultureInfo.InvariantCulture, value);

net/Sample/Controllers/NorthwindController.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
using DevExtreme.AspNet.Data;
22

3-
using Sample.Models;
4-
53
using Microsoft.AspNetCore.Mvc;
64
using Microsoft.EntityFrameworkCore;
75

86
using Newtonsoft.Json;
97

8+
using Sample.Models;
9+
1010
namespace Sample.Controllers {
1111

1212
[Route("nwind")]
@@ -23,6 +23,10 @@ public async Task<IActionResult> Orders(DataSourceLoadOptions loadOptions) {
2323
o.OrderId,
2424
o.CustomerId,
2525
o.OrderDate,
26+
//----------------------------------------
27+
o.OrderDateOnly,
28+
o.OrderTimeOnly,
29+
//----------------------------------------
2630
o.Freight,
2731
o.ShipCountry,
2832
o.ShipRegion,

net/Sample/Converters.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#if NET6_0
2+
using System.Text.Json;
3+
using System.Text.Json.Serialization;
4+
#endif
5+
6+
#if NET6_0 || NET7_0
7+
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
8+
#endif
9+
10+
#if NET6_0
11+
// TODO: Remove after migrate to net7+
12+
// https://devblogs.microsoft.com/dotnet/system-text-json-in-dotnet-7/
13+
// https://stackoverflow.com/questions/74246482/
14+
public sealed class Net6DateOnlyJsonConverter : JsonConverter<DateOnly> {
15+
public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => DateOnly.FromDateTime(reader.GetDateTime());
16+
public override void Write(Utf8JsonWriter writer, DateOnly value, JsonSerializerOptions options) => writer.WriteStringValue(value.ToString("O"));
17+
}
18+
public sealed class Net6TimeOnlyJsonConverter : JsonConverter<TimeOnly> {
19+
public override TimeOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => TimeOnly.Parse(reader.GetString());
20+
public override void Write(Utf8JsonWriter writer, TimeOnly value, JsonSerializerOptions options) => writer.WriteStringValue(value.ToString("HH:mm:ss.fff"));
21+
}
22+
#endif
23+
24+
#if NET6_0 || NET7_0
25+
public sealed class NetLess8DateOnlyValueConverter : ValueConverter<DateOnly, DateTime> {
26+
public NetLess8DateOnlyValueConverter() : base(
27+
dateOnly => dateOnly.ToDateTime(TimeOnly.MinValue),
28+
dateTime => DateOnly.FromDateTime(dateTime)) { }
29+
}
30+
31+
public sealed class NetLess8TimeOnlyValueConverter : ValueConverter<TimeOnly, TimeSpan> {
32+
public NetLess8TimeOnlyValueConverter() : base(
33+
timeOnly => timeOnly.ToTimeSpan(),
34+
timeSpan => TimeOnly.FromTimeSpan(timeSpan)) { }
35+
}
36+
#endif

net/Sample/Models/NorthwindContext.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) {
125125
.HasConstraintName("FK_Products_Categories");
126126
});
127127
}
128+
129+
#if NET6_0 || NET7_0
130+
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) {
131+
base.ConfigureConventions(configurationBuilder);
132+
configurationBuilder.Properties<DateOnly>().HaveConversion<NetLess8DateOnlyValueConverter>();
133+
configurationBuilder.Properties<TimeOnly>().HaveConversion<NetLess8TimeOnlyValueConverter>();
134+
}
135+
#endif
136+
128137
}
129138
}

0 commit comments

Comments
 (0)