Skip to content

Commit

Permalink
Support for DateOnly/TimeOnly for SQL Server
Browse files Browse the repository at this point in the history
  • Loading branch information
roji committed Jan 22, 2023
1 parent 4db857b commit 584591f
Show file tree
Hide file tree
Showing 26 changed files with 1,878 additions and 413 deletions.
2 changes: 1 addition & 1 deletion src/EFCore.SqlServer/EFCore.SqlServer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.0.1" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.1.0" />
</ItemGroup>

<ItemGroup>
Expand Down
686 changes: 669 additions & 17 deletions src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ private readonly Dictionary<MethodInfo, string> _methodInfoDateDiffMapping
new[] { typeof(DbFunctions), typeof(DateTimeOffset?), typeof(DateTimeOffset?) })!,
"year"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffYear),
new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) })!,
"year"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffYear),
new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) })!,
"year"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffMonth),
Expand All @@ -65,6 +77,18 @@ private readonly Dictionary<MethodInfo, string> _methodInfoDateDiffMapping
new[] { typeof(DbFunctions), typeof(DateTimeOffset?), typeof(DateTimeOffset?) })!,
"month"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffMonth),
new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) })!,
"month"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffMonth),
new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) })!,
"month"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffDay), new[] { typeof(DbFunctions), typeof(DateTime), typeof(DateTime) })!,
Expand All @@ -88,6 +112,18 @@ private readonly Dictionary<MethodInfo, string> _methodInfoDateDiffMapping
new[] { typeof(DbFunctions), typeof(DateTimeOffset?), typeof(DateTimeOffset?) })!,
"day"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffDay),
new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) })!,
"day"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffDay),
new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) })!,
"day"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffHour),
Expand Down Expand Up @@ -124,6 +160,30 @@ private readonly Dictionary<MethodInfo, string> _methodInfoDateDiffMapping
new[] { typeof(DbFunctions), typeof(TimeSpan?), typeof(TimeSpan?) })!,
"hour"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffHour),
new[] { typeof(DbFunctions), typeof(TimeOnly), typeof(TimeOnly) })!,
"hour"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffHour),
new[] { typeof(DbFunctions), typeof(TimeOnly?), typeof(TimeOnly?) })!,
"hour"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffHour),
new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) })!,
"hour"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffHour),
new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) })!,
"hour"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffMinute),
Expand Down Expand Up @@ -160,6 +220,30 @@ private readonly Dictionary<MethodInfo, string> _methodInfoDateDiffMapping
new[] { typeof(DbFunctions), typeof(TimeSpan?), typeof(TimeSpan?) })!,
"minute"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffMinute),
new[] { typeof(DbFunctions), typeof(TimeOnly), typeof(TimeOnly) })!,
"minute"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffMinute),
new[] { typeof(DbFunctions), typeof(TimeOnly?), typeof(TimeOnly?) })!,
"minute"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffMinute),
new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) })!,
"minute"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffMinute),
new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) })!,
"minute"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffSecond),
Expand Down Expand Up @@ -196,6 +280,30 @@ private readonly Dictionary<MethodInfo, string> _methodInfoDateDiffMapping
new[] { typeof(DbFunctions), typeof(TimeSpan?), typeof(TimeSpan?) })!,
"second"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffSecond),
new[] { typeof(DbFunctions), typeof(TimeOnly), typeof(TimeOnly) })!,
"second"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffSecond),
new[] { typeof(DbFunctions), typeof(TimeOnly?), typeof(TimeOnly?) })!,
"second"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffSecond),
new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) })!,
"second"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffSecond),
new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) })!,
"second"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffMillisecond),
Expand Down Expand Up @@ -232,6 +340,30 @@ private readonly Dictionary<MethodInfo, string> _methodInfoDateDiffMapping
new[] { typeof(DbFunctions), typeof(TimeSpan?), typeof(TimeSpan?) })!,
"millisecond"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffMillisecond),
new[] { typeof(DbFunctions), typeof(TimeOnly), typeof(TimeOnly) })!,
"millisecond"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffMillisecond),
new[] { typeof(DbFunctions), typeof(TimeOnly?), typeof(TimeOnly?) })!,
"millisecond"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffMillisecond),
new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) })!,
"millisecond"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffMillisecond),
new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) })!,
"millisecond"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffMicrosecond),
Expand Down Expand Up @@ -268,6 +400,30 @@ private readonly Dictionary<MethodInfo, string> _methodInfoDateDiffMapping
new[] { typeof(DbFunctions), typeof(TimeSpan?), typeof(TimeSpan?) })!,
"microsecond"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffMicrosecond),
new[] { typeof(DbFunctions), typeof(TimeOnly), typeof(TimeOnly) })!,
"microsecond"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffMicrosecond),
new[] { typeof(DbFunctions), typeof(TimeOnly?), typeof(TimeOnly?) })!,
"microsecond"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffMicrosecond),
new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) })!,
"microsecond"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffMicrosecond),
new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) })!,
"microsecond"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffNanosecond),
Expand Down Expand Up @@ -304,6 +460,30 @@ private readonly Dictionary<MethodInfo, string> _methodInfoDateDiffMapping
new[] { typeof(DbFunctions), typeof(TimeSpan?), typeof(TimeSpan?) })!,
"nanosecond"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffNanosecond),
new[] { typeof(DbFunctions), typeof(TimeOnly), typeof(TimeOnly) })!,
"nanosecond"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffNanosecond),
new[] { typeof(DbFunctions), typeof(TimeOnly?), typeof(TimeOnly?) })!,
"nanosecond"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffNanosecond),
new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) })!,
"nanosecond"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffNanosecond),
new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) })!,
"nanosecond"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffWeek),
Expand All @@ -327,6 +507,18 @@ private readonly Dictionary<MethodInfo, string> _methodInfoDateDiffMapping
nameof(SqlServerDbFunctionsExtensions.DateDiffWeek),
new[] { typeof(DbFunctions), typeof(DateTimeOffset?), typeof(DateTimeOffset?) })!,
"week"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffWeek),
new[] { typeof(DbFunctions), typeof(DateOnly), typeof(DateOnly) })!,
"week"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffWeek),
new[] { typeof(DbFunctions), typeof(DateOnly?), typeof(DateOnly?) })!,
"week"
}
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.Query.SqlExpressions;

namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public class SqlServerDateOnlyMemberTranslator : IMemberTranslator
{
private static readonly Dictionary<string, string> DatePartMapping
= new()
{
{ nameof(DateTime.Year), "year" },
{ nameof(DateTime.Month), "month" },
{ nameof(DateTime.DayOfYear), "dayofyear" },
{ nameof(DateTime.Day), "day" }
};

private readonly ISqlExpressionFactory _sqlExpressionFactory;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public SqlServerDateOnlyMemberTranslator(ISqlExpressionFactory sqlExpressionFactory)
{
_sqlExpressionFactory = sqlExpressionFactory;
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public SqlExpression? Translate(
SqlExpression? instance,
MemberInfo member,
Type returnType,
IDiagnosticsLogger<DbLoggerCategory.Query> logger)
=> member.DeclaringType == typeof(DateOnly) && DatePartMapping.TryGetValue(member.Name, out var datePart)
? _sqlExpressionFactory.Function(
"DATEPART",
new[] { _sqlExpressionFactory.Fragment(datePart), instance! },
nullable: true,
argumentsPropagateNullability: new[] { false, true },
returnType)
: null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.Query.SqlExpressions;

namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public class SqlServerDateOnlyMethodTranslator : IMethodCallTranslator
{
private readonly Dictionary<MethodInfo, string> _methodInfoDatePartMapping = new()
{
{ typeof(DateOnly).GetRuntimeMethod(nameof(DateOnly.AddYears), new[] { typeof(int) })!, "year" },
{ typeof(DateOnly).GetRuntimeMethod(nameof(DateOnly.AddMonths), new[] { typeof(int) })!, "month" },
{ typeof(DateOnly).GetRuntimeMethod(nameof(DateOnly.AddDays), new[] { typeof(int) })!, "day" }
};

private readonly ISqlExpressionFactory _sqlExpressionFactory;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public SqlServerDateOnlyMethodTranslator(ISqlExpressionFactory sqlExpressionFactory)
{
_sqlExpressionFactory = sqlExpressionFactory;
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual SqlExpression? Translate(
SqlExpression? instance,
MethodInfo method,
IReadOnlyList<SqlExpression> arguments,
IDiagnosticsLogger<DbLoggerCategory.Query> logger)
{
if (_methodInfoDatePartMapping.TryGetValue(method, out var datePart)
&& instance != null)
{
instance = _sqlExpressionFactory.ApplyDefaultTypeMapping(instance);

return _sqlExpressionFactory.Function(
"DATEADD",
new[] { _sqlExpressionFactory.Fragment(datePart), _sqlExpressionFactory.Convert(arguments[0], typeof(int)), instance },
nullable: true,
argumentsPropagateNullability: new[] { false, true, true },
instance.Type,
instance.TypeMapping);
}

return null;
}
}
Loading

0 comments on commit 584591f

Please sign in to comment.