diff --git a/src/EFCore.Jet/Query/ExpressionTranslators/Internal/JetDateTimeMemberTranslator.cs b/src/EFCore.Jet/Query/ExpressionTranslators/Internal/JetDateTimeMemberTranslator.cs index 5abcb53..af00352 100644 --- a/src/EFCore.Jet/Query/ExpressionTranslators/Internal/JetDateTimeMemberTranslator.cs +++ b/src/EFCore.Jet/Query/ExpressionTranslators/Internal/JetDateTimeMemberTranslator.cs @@ -7,6 +7,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query.SqlExpressions; +using Microsoft.EntityFrameworkCore.Storage; namespace EntityFrameworkCore.Jet.Query.ExpressionTranslators.Internal { @@ -16,10 +17,30 @@ namespace EntityFrameworkCore.Jet.Query.ExpressionTranslators.Internal /// public class JetDateTimeMemberTranslator : IMemberTranslator { + private static readonly Dictionary DatePartMapping + = new() + { + { nameof(DateTime.Year), "yyyy" }, + { nameof(DateTime.Month), "m" }, + { nameof(DateTime.DayOfYear), "y" }, + { nameof(DateTime.Day), "d" }, + { nameof(DateTime.Hour), "h" }, + { nameof(DateTime.Minute), "n" }, + { nameof(DateTime.Second), "s" }, + { nameof(DateTime.DayOfWeek), "w" }, + //{ nameof(DateTime.Millisecond), "millisecond" } + }; + private readonly JetSqlExpressionFactory _sqlExpressionFactory; + private readonly IRelationalTypeMappingSource _typeMappingSource; - public JetDateTimeMemberTranslator(ISqlExpressionFactory sqlExpressionFactory) - => _sqlExpressionFactory = (JetSqlExpressionFactory)sqlExpressionFactory; + public JetDateTimeMemberTranslator( + ISqlExpressionFactory sqlExpressionFactory, + IRelationalTypeMappingSource typeMappingSource) + { + _sqlExpressionFactory = (JetSqlExpressionFactory)sqlExpressionFactory; + _typeMappingSource = typeMappingSource; + } /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used @@ -30,20 +51,34 @@ namespace EntityFrameworkCore.Jet.Query.ExpressionTranslators.Internal if (member.DeclaringType == typeof(DateTime) || member.DeclaringType == typeof(DateTimeOffset)) { - if (instance == null) + if (DatePartMapping.TryGetValue(member.Name, out var datePart)) { - return member.Name switch + var datePartFunc = _sqlExpressionFactory.Function( + "DATEPART", + new[] + { + _sqlExpressionFactory.Constant(datePart), + instance!, + }, + false, + new[] { false }, + returnType); + if (datePart == "w") { - nameof(DateTime.Now) => _sqlExpressionFactory.Function("NOW", Array.Empty(), - false, new[] { false }, returnType), - nameof(DateTime.UtcNow) => _sqlExpressionFactory.Function("NOW", Array.Empty(), - false, new[] { false }, returnType), - _ => null, - }; + return _sqlExpressionFactory.Subtract( + datePartFunc, + _sqlExpressionFactory.Constant(1)); + } + + return datePartFunc; } return member.Name switch { + nameof(DateTime.Now) => _sqlExpressionFactory.Function("NOW", Array.Empty(), + false, new[] { false }, returnType), + nameof(DateTime.UtcNow) => _sqlExpressionFactory.Function("NOW", Array.Empty(), + false, new[] { false }, returnType), nameof(DateTime.Today) => _sqlExpressionFactory.Function( "DATEVALUE", new[] @@ -55,32 +90,19 @@ namespace EntityFrameworkCore.Jet.Query.ExpressionTranslators.Internal new[] { false }, returnType), - nameof(DateTime.Year) => GetDatePartExpression(instance, returnType, "yyyy"), - nameof(DateTime.Month) => GetDatePartExpression(instance, returnType, "m"), - nameof(DateTime.DayOfYear) => GetDatePartExpression(instance, returnType, "y"), - nameof(DateTime.Day) => GetDatePartExpression(instance, returnType, "d"), - nameof(DateTime.Hour) => GetDatePartExpression(instance, returnType, "h"), - nameof(DateTime.Minute) => GetDatePartExpression(instance, returnType, "n"), - nameof(DateTime.Second) => GetDatePartExpression(instance, returnType, "s"), - nameof(DateTime.Millisecond) => null, // Not supported in Jet - - nameof(DateTime.DayOfWeek) => _sqlExpressionFactory.Subtract( - GetDatePartExpression(instance, returnType, "w"), - _sqlExpressionFactory.Constant(1)), - nameof(DateTime.Date) => _sqlExpressionFactory.NullChecked( - instance, + instance!, _sqlExpressionFactory.Function( "DATEVALUE", - new[] { instance }, + new[] { instance! }, false, new[] { false }, returnType)), nameof(DateTime.TimeOfDay) => _sqlExpressionFactory.NullChecked( - instance, + instance!, _sqlExpressionFactory.Function( "TIMEVALUE", - new[] { instance }, + new[] { instance! }, false, new[] { false }, returnType)), @@ -92,22 +114,5 @@ namespace EntityFrameworkCore.Jet.Query.ExpressionTranslators.Internal } return null; } - - private SqlExpression GetDatePartExpression( - SqlExpression instance, - Type returnType, - string datePart) - { - return _sqlExpressionFactory.Function( - "DATEPART", - new[] - { - _sqlExpressionFactory.Constant(datePart), - instance, - }, - false, - new[] { false }, - returnType); - } } } \ No newline at end of file diff --git a/src/EFCore.Jet/Query/ExpressionTranslators/Internal/JetMemberTranslatorProvider.cs b/src/EFCore.Jet/Query/ExpressionTranslators/Internal/JetMemberTranslatorProvider.cs index 4e00771..c437bb9 100644 --- a/src/EFCore.Jet/Query/ExpressionTranslators/Internal/JetMemberTranslatorProvider.cs +++ b/src/EFCore.Jet/Query/ExpressionTranslators/Internal/JetMemberTranslatorProvider.cs @@ -2,6 +2,7 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Query; +using Microsoft.EntityFrameworkCore.Storage; namespace EntityFrameworkCore.Jet.Query.ExpressionTranslators.Internal { @@ -15,16 +16,15 @@ namespace EntityFrameworkCore.Jet.Query.ExpressionTranslators.Internal /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// - public JetMemberTranslatorProvider([NotNull] RelationalMemberTranslatorProviderDependencies dependencies) + public JetMemberTranslatorProvider([NotNull] RelationalMemberTranslatorProviderDependencies dependencies, IRelationalTypeMappingSource typeMappingSource) : base(dependencies) { var sqlExpressionFactory = (JetSqlExpressionFactory)dependencies.SqlExpressionFactory; - // ReSharper disable once VirtualMemberCallInConstructor AddTranslators(new IMemberTranslator[] { new JetDateOnlyMemberTranslator(sqlExpressionFactory), - new JetDateTimeMemberTranslator(sqlExpressionFactory), + new JetDateTimeMemberTranslator(sqlExpressionFactory, typeMappingSource), new JetStringMemberTranslator(sqlExpressionFactory), new JetTimeSpanMemberTranslator(sqlExpressionFactory), new JetTimeOnlyMemberTranslator(sqlExpressionFactory) diff --git a/test/EFCore.Jet.FunctionalTests/Query/NorthwindWhereQueryJetTest.cs b/test/EFCore.Jet.FunctionalTests/Query/NorthwindWhereQueryJetTest.cs index ae4135b..756bb14 100644 --- a/test/EFCore.Jet.FunctionalTests/Query/NorthwindWhereQueryJetTest.cs +++ b/test/EFCore.Jet.FunctionalTests/Query/NorthwindWhereQueryJetTest.cs @@ -1,5 +1,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Threading.Tasks; using EntityFrameworkCore.Jet.FunctionalTests.TestUtilities; using Microsoft.EntityFrameworkCore.Query; @@ -817,9 +818,11 @@ WHERE CAST(SYSUTCDATETIME() AS datetimeoffset) <> @__myDatetimeOffset_0 await base.Where_datetime_today(isAsync); AssertSql( - $@"SELECT `e`.`EmployeeID`, `e`.`City`, `e`.`Country`, `e`.`FirstName`, `e`.`ReportsTo`, `e`.`Title` +""" +SELECT `e`.`EmployeeID`, `e`.`City`, `e`.`Country`, `e`.`FirstName`, `e`.`ReportsTo`, `e`.`Title` FROM `Employees` AS `e` -WHERE (CONVERT(date, GETDATE()) = CONVERT(date, GETDATE())) OR CONVERT(date, GETDATE()) IS NULL"); +WHERE DATEVALUE(NOW()) = DATEVALUE(DATE()) +"""); } public override async Task Where_datetime_date_component(bool isAsync)