Skip to content

Commit a817062

Browse files
authored
Merge pull request #1184 from rhuijben/feat/full-DateOnly-support
Add full DateOnly support
2 parents 91a5d3f + 583a73f commit a817062

File tree

4 files changed

+166
-8
lines changed

4 files changed

+166
-8
lines changed

RepoDb.Core/RepoDb/Extensions/DbCommandExtension.cs

+10
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,12 @@ private static object AutomaticConvert(object value,
885885
{
886886
return AutomaticConvertGuidToString(value);
887887
}
888+
#if NET6_0_OR_GREATER
889+
else if (fromType == StaticType.DateOnly && targetType == StaticType.DateTime)
890+
{
891+
return AutomaticConvertDateOnlyToDateTime(value);
892+
}
893+
#endif
888894
else
889895
{
890896
return (value != DBNull.Value) ? Convert.ChangeType(value, targetType) : Activator.CreateInstance(targetType);
@@ -915,6 +921,10 @@ private static object AutomaticConvertStringToGuid(object value)
915921
private static object AutomaticConvertGuidToString(object value) =>
916922
value?.ToString();
917923

924+
#if NET6_0_OR_GREATER
925+
private static object AutomaticConvertDateOnlyToDateTime(object value) =>
926+
(value is DateOnly dateOnly ? dateOnly.ToDateTime(default(TimeOnly)) : null);
927+
#endif
918928
#endregion
919929
}
920930
}

RepoDb.Core/RepoDb/Extensions/ExpressionExtension.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ public static object GetValue(this NewExpression expression)
382382
if (expression.Arguments.Count > 0)
383383
{
384384
return Activator.CreateInstance(expression.Type,
385-
expression.Arguments.Select(arg => arg.GetValue()));
385+
expression.Arguments.Select(arg => arg.GetValue()).ToArray());
386386
}
387387
else
388388
{

RepoDb.Core/RepoDb/Reflection/Compiler/Compiler.cs

+55-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1+
using RepoDb.Enumerations;
2+
using RepoDb.Exceptions;
3+
using RepoDb.Extensions;
4+
using RepoDb.Interfaces;
5+
using RepoDb.Resolvers;
16
using System;
27
using System.Collections.Generic;
38
using System.Data;
49
using System.Data.Common;
510
using System.Linq;
611
using System.Linq.Expressions;
712
using System.Reflection;
8-
using RepoDb.Enumerations;
9-
using RepoDb.Exceptions;
10-
using RepoDb.Extensions;
11-
using RepoDb.Interfaces;
12-
using RepoDb.Resolvers;
1313

1414
namespace RepoDb.Reflection
1515
{
@@ -510,7 +510,15 @@ internal static MethodInfo GetDateTimeTimeOfDayPropertyGetMethod() =>
510510
/// <returns></returns>
511511
internal static MethodInfo GetDateOnlyFromDateTimeStaticMethod() =>
512512
StaticType.DateOnly.GetMethod("FromDateTime");
513+
514+
/// <summary>
515+
///
516+
/// </summary>
517+
/// <returns></returns>
518+
internal static MethodInfo GetDateTimeFromDateOnlyMethod() =>
519+
StaticType.DateOnly.GetMethod("ToDateTime", new Type[] { StaticType.TimeOnly });
513520
#endif
521+
514522
/// <summary>
515523
///
516524
/// </summary>
@@ -548,6 +556,24 @@ internal static Expression ConvertExpressionToNullableGetValueOrDefaultExpressio
548556
return expression;
549557
}
550558

559+
#if NET6_0_OR_GREATER
560+
internal static Expression ConvertExpressionToNullableGetValueOrDefaultExpression(Func<Expression, Expression> converter, Expression expression)
561+
{
562+
if (Nullable.GetUnderlyingType(expression.Type) != null)
563+
{
564+
var converted = converter(ConvertExpressionToNullableGetValueOrDefaultExpression(expression));
565+
var nullableType = typeof(Nullable<>).MakeGenericType(converted.Type);
566+
return Expression.Condition(
567+
Expression.Property(expression, nameof(Nullable<int>.HasValue)),
568+
Expression.Convert(converted, nullableType),
569+
Expression.Constant(null, nullableType)
570+
);
571+
}
572+
573+
return converter(expression);
574+
}
575+
#endif
576+
551577
/// <summary>
552578
///
553579
/// </summary>
@@ -601,7 +627,15 @@ internal static Expression ConvertExpressionToDateTimeToTimeSpanExpression(Expre
601627
/// <param name="expression"></param>
602628
/// <returns></returns>
603629
internal static Expression ConvertExpressionToDateTimeToDateOnlyExpression(Expression expression) =>
604-
ConvertExpressionToNullableGetValueOrDefaultExpression(ConvertExpressionToDateOnlyFromDateTimeExpression(expression));
630+
ConvertExpressionToNullableGetValueOrDefaultExpression(ConvertExpressionToDateTimeFromDateOnlyExpression, expression);
631+
632+
/// <summary>
633+
///
634+
/// </summary>
635+
/// <param name="expression"></param>
636+
/// <returns></returns>
637+
internal static Expression ConvertExpressionToDateOnlyToDateTimeExpression(Expression expression) =>
638+
ConvertExpressionToNullableGetValueOrDefaultExpression(ConvertExpressionToDateOnlyFromDateTimeExpression, expression);
605639
#endif
606640
/// <summary>
607641
///
@@ -625,6 +659,14 @@ internal static Expression ConvertExpressionToDateTimeTimeOfDayExpression(Expres
625659
/// <param name="expression"></param>
626660
/// <returns></returns>
627661
internal static Expression ConvertExpressionToDateOnlyFromDateTimeExpression(Expression expression) =>
662+
Expression.Call(expression, GetDateTimeFromDateOnlyMethod(), Expression.Constant(default(TimeOnly)));
663+
664+
/// <summary>
665+
///
666+
/// </summary>
667+
/// <param name="expression"></param>
668+
/// <returns></returns>
669+
internal static Expression ConvertExpressionToDateTimeFromDateOnlyExpression(Expression expression) =>
628670
Expression.Call(null, GetDateOnlyFromDateTimeStaticMethod(), expression);
629671
#endif
630672
/// <summary>
@@ -907,6 +949,12 @@ internal static Expression ConvertExpressionWithAutomaticConversion(Expression e
907949
{
908950
expression = ConvertExpressionToDateTimeToDateOnlyExpression(expression);
909951
}
952+
953+
// DateOnly to DateTime
954+
else if (fromType == StaticType.DateOnly && toType == StaticType.DateTime)
955+
{
956+
expression = ConvertExpressionToDateOnlyToDateTimeExpression(expression);
957+
}
910958
#endif
911959
// Others
912960
else
@@ -1084,7 +1132,7 @@ internal static Expression ConvertExpressionToClassHandlerSetExpression(Expressi
10841132
return entityOrEntitiesExpression;
10851133
}
10861134

1087-
#endregion
1135+
#endregion
10881136

10891137
#region Common
10901138

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
using Microsoft.Data.SqlClient;
2+
using Microsoft.VisualStudio.TestTools.UnitTesting;
3+
using RepoDb.SqlServer.IntegrationTests.Setup;
4+
using System;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
8+
namespace RepoDb.SqlServer.IntegrationTests
9+
{
10+
[TestClass]
11+
public class AdditionalDbTypesTests
12+
{
13+
[TestInitialize]
14+
public void Initialize()
15+
{
16+
GlobalConfiguration
17+
.Setup(new()
18+
{
19+
ConversionType = Enumerations.ConversionType.Automatic
20+
})
21+
.UseSqlServer();
22+
23+
Database.Initialize();
24+
Cleanup();
25+
26+
using var connection = new SqlConnection(Database.ConnectionString).EnsureOpen();
27+
28+
connection.ExecuteNonQuery($@"
29+
IF (NOT EXISTS(SELECT 1 FROM [sys].[objects] WHERE type = 'U' AND name = '{nameof(DateOnlyTestData)}'))
30+
BEGIN
31+
CREATE TABLE [dbo].[{nameof(DateOnlyTestData)}] (
32+
[Id] INT IDENTITY(1, 1),
33+
[DateOnly] DATE NOT NULL,
34+
[DateOnlyNullable] DATE NULL,
35+
CONSTRAINT [{nameof(DateOnlyTestData)}_Id] PRIMARY KEY
36+
(
37+
[Id] ASC
38+
)
39+
) ON [PRIMARY]
40+
END");
41+
42+
// Do this again as this is now overwritten
43+
GlobalConfiguration
44+
.Setup(new()
45+
{
46+
ConversionType = Enumerations.ConversionType.Automatic
47+
})
48+
.UseSqlServer();
49+
}
50+
51+
[TestCleanup]
52+
public void Cleanup()
53+
{
54+
Database.Cleanup();
55+
}
56+
57+
[TestMethod]
58+
public async Task TestDateTimeOnlyInsertQuery()
59+
{
60+
await using var connection = new SqlConnection(Database.ConnectionString);
61+
await connection.OpenAsync();
62+
await using var t = await connection.BeginTransactionAsync();
63+
64+
await connection.InsertAllAsync(
65+
new DateOnlyTestData[] {
66+
new()
67+
{
68+
DateOnly = new DateOnly(2024,1,1),
69+
DateOnlyNullable = null,
70+
},
71+
new ()
72+
{
73+
DateOnly = new DateOnly(2025,1,1),
74+
DateOnlyNullable = new DateOnly(2026,1,1),
75+
}
76+
},
77+
transaction: t);
78+
79+
80+
var all = await connection.QueryAllAsync<DateOnlyTestData>(transaction: t);
81+
82+
Assert.IsTrue(all.Any(x => x.DateOnly == new DateOnly(2024, 1, 1)), "Found DateOnly");
83+
Assert.IsTrue(all.Any(x => x.DateOnlyNullable == new DateOnly(2026, 1, 1)), "Found nullable DateOnly");
84+
Assert.IsTrue(all.Any(x => x.DateOnlyNullable == null), "Found null DateOnly?");
85+
86+
var cmp1 = new DateOnly(2024, 1, 1);
87+
Assert.AreEqual(1, (await connection.QueryAsync<DateOnlyTestData>(where: x => x.DateOnly == cmp1, transaction: t)).Count());
88+
Assert.IsTrue((await connection.QueryAsync<DateOnlyTestData>(where: x => x.DateOnlyNullable == new DateOnly(2026, 1, 1), transaction: t)).Count() == 1);
89+
}
90+
91+
92+
93+
public class DateOnlyTestData
94+
{
95+
public int ID { get; set; }
96+
public DateOnly DateOnly { get; set; }
97+
public DateOnly? DateOnlyNullable { get; set; }
98+
}
99+
}
100+
}

0 commit comments

Comments
 (0)