Skip to content

Commit

Permalink
Support for DateOnly/TimeOnly for SQL Server (#30109)
Browse files Browse the repository at this point in the history
Closes #24507
  • Loading branch information
roji authored Jan 30, 2023
1 parent 8188627 commit 827c182
Show file tree
Hide file tree
Showing 51 changed files with 3,626 additions and 1,150 deletions.
3 changes: 3 additions & 0 deletions All.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ The .NET Foundation licenses this file to you under the MIT license.
<s:Boolean x:Key="/Default/UserDictionary/Words/=composability/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=composable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Constructible/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=DATEADD/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=datetimeoffset/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=doesnt/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=efcore/@EntryIndexedValue">True</s:Boolean>
Expand All @@ -312,6 +313,7 @@ The .NET Foundation licenses this file to you under the MIT license.&#xD;
<s:Boolean x:Key="/Default/UserDictionary/Words/=requiredness/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=retriable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Reuniquify/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=rowversion/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=sargable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=savepoints/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Scaffolder/@EntryIndexedValue">True</s:Boolean>
Expand All @@ -328,6 +330,7 @@ The .NET Foundation licenses this file to you under the MIT license.&#xD;
<s:Boolean x:Key="/Default/UserDictionary/Words/=unignore/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=fixup/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=attacher/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=uniqueidentifier/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Uniquification/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Uniquified/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Uniquifier/@EntryIndexedValue">True</s:Boolean>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Data.SqlTypes;
using NetTopologySuite.Geometries;

namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
Expand Down Expand Up @@ -43,7 +44,8 @@ public SqlServerNetTopologySuiteTypeMappingSourcePlugin(NtsGeometryServices geom
|| (storeTypeName != null
&& _spatialStoreTypes.Contains(storeTypeName))
? (RelationalTypeMapping)Activator.CreateInstance(
typeof(SqlServerGeometryTypeMapping<>).MakeGenericType(clrType ?? typeof(Geometry)),
typeof(SqlServerGeometryTypeMapping<>).MakeGenericType(
clrType is null || clrType == typeof(SqlBytes) ? typeof(Geometry) : clrType),
_geometryServices,
storeTypeName ?? "geography")!
: null;
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 virtual 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;
}
Loading

0 comments on commit 827c182

Please sign in to comment.