diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosMemberTranslatorProvider.cs b/src/EFCore.Cosmos/Query/Internal/CosmosMemberTranslatorProvider.cs
index 3acf24c955a..b8c8f8c042b 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosMemberTranslatorProvider.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosMemberTranslatorProvider.cs
@@ -1,6 +1,8 @@
// 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.Internal;
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
@@ -27,8 +29,9 @@ public CosmosMemberTranslatorProvider(
_plugins.AddRange(plugins.SelectMany(p => p.Translators));
_translators.AddRange(
[
- new CosmosStringMemberTranslator(sqlExpressionFactory),
- new CosmosDateTimeMemberTranslator(sqlExpressionFactory)
+ new CosmosDateTimeMemberTranslator(sqlExpressionFactory),
+ new CosmosNullableMemberTranslator(sqlExpressionFactory),
+ new CosmosStringMemberTranslator(sqlExpressionFactory)
]);
}
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosMethodCallTranslatorProvider.cs b/src/EFCore.Cosmos/Query/Internal/CosmosMethodCallTranslatorProvider.cs
index 65ea3e9d188..0d871e9230a 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosMethodCallTranslatorProvider.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosMethodCallTranslatorProvider.cs
@@ -28,11 +28,12 @@ public CosmosMethodCallTranslatorProvider(
_translators.AddRange(
[
+ new CosmosDateTimeMethodTranslator(sqlExpressionFactory),
new CosmosEqualsTranslator(sqlExpressionFactory),
- new CosmosStringMethodTranslator(sqlExpressionFactory),
- new CosmosRandomTranslator(sqlExpressionFactory),
new CosmosMathTranslator(sqlExpressionFactory),
+ new CosmosRandomTranslator(sqlExpressionFactory),
new CosmosRegexTranslator(sqlExpressionFactory),
+ new CosmosStringMethodTranslator(sqlExpressionFactory),
new CosmosTypeCheckingTranslator(sqlExpressionFactory)
//new LikeTranslator(sqlExpressionFactory),
//new EnumHasFlagTranslator(sqlExpressionFactory),
diff --git a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosDateTimeMemberTranslator.cs b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosDateTimeMemberTranslator.cs
index eaa98ab331c..8a460c8547c 100644
--- a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosDateTimeMemberTranslator.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosDateTimeMemberTranslator.cs
@@ -25,16 +25,34 @@ public class CosmosDateTimeMemberTranslator(ISqlExpressionFactory sqlExpressionF
IDiagnosticsLogger logger)
{
var declaringType = member.DeclaringType;
- if ((declaringType == typeof(DateTime)
- || declaringType == typeof(DateTimeOffset))
- && member.Name == nameof(DateTime.UtcNow))
+
+ if (declaringType != typeof(DateTime) && declaringType != typeof(DateTimeOffset))
{
- return sqlExpressionFactory.Function(
- "GetCurrentDateTime",
- [],
- returnType);
+ return null;
}
- return null;
+ return member.Name switch
+ {
+ nameof(DateTime.Year) => DatePart("yyyy"),
+ nameof(DateTime.Month) => DatePart("mm"),
+ nameof(DateTime.Day) => DatePart("dd"),
+ nameof(DateTime.Hour) => DatePart("hh"),
+ nameof(DateTime.Minute) => DatePart("mi"),
+ nameof(DateTime.Second) => DatePart("ss"),
+ nameof(DateTime.Millisecond) => DatePart("ms"),
+ nameof(DateTime.Microsecond) => DatePart("mcs"),
+ nameof(DateTime.Nanosecond) => DatePart("ns"),
+
+ nameof(DateTime.UtcNow)
+ => sqlExpressionFactory.Function(
+ "GetCurrentDateTime",
+ [],
+ returnType),
+
+ _ => null
+ };
+
+ SqlFunctionExpression DatePart(string part)
+ => sqlExpressionFactory.Function("DateTimePart", arguments: [sqlExpressionFactory.Constant(part), instance!], returnType);
}
}
diff --git a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosDateTimeMethodTranslator.cs b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosDateTimeMethodTranslator.cs
new file mode 100644
index 00000000000..bbec5002a50
--- /dev/null
+++ b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosDateTimeMethodTranslator.cs
@@ -0,0 +1,70 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// ReSharper disable once CheckNamespace
+namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
+
+///
+/// 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.
+///
+public class CosmosDateTimeMethodTranslator(ISqlExpressionFactory sqlExpressionFactory) : IMethodCallTranslator
+{
+ private static readonly Dictionary MethodInfoDatePartMapping = new()
+ {
+ { typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddYears), [typeof(int)])!, "yyyy" },
+ { typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddMonths), [typeof(int)])!, "mm" },
+ { typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddDays), [typeof(double)])!, "dd" },
+ { typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddHours), [typeof(double)])!, "hh" },
+ { typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddMinutes), [typeof(double)])!, "mi" },
+ { typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddSeconds), [typeof(double)])!, "ss" },
+ { typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddMilliseconds), [typeof(double)])!, "ms" },
+ { typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddMicroseconds), [typeof(double)])!, "mcs" },
+ { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddYears), [typeof(int)])!, "yyyy" },
+ { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddMonths), [typeof(int)])!, "mm" },
+ { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddDays), [typeof(double)])!, "dd" },
+ { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddHours), [typeof(double)])!, "hh" },
+ { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddMinutes), [typeof(double)])!, "mi" },
+ { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddSeconds), [typeof(double)])!, "ss" },
+ { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddMilliseconds), [typeof(double)])!, "ms" },
+ { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddMicroseconds), [typeof(double)])!, "mcs" }
+ };
+
+ private static readonly Dictionary MethodInfoDateDiffMapping = new()
+ {
+ { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.ToUnixTimeSeconds), Type.EmptyTypes)!, "second" },
+ { typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.ToUnixTimeMilliseconds), Type.EmptyTypes)!, "millisecond" }
+ };
+
+ ///
+ /// 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.
+ ///
+ public SqlExpression? Translate(
+ SqlExpression? instance,
+ MethodInfo method,
+ IReadOnlyList arguments,
+ IDiagnosticsLogger logger)
+ {
+ if (method.DeclaringType != typeof(DateTime) && method.DeclaringType != typeof(DateTimeOffset))
+ {
+ return null;
+ }
+
+ if (MethodInfoDatePartMapping.TryGetValue(method, out var datePart)
+ && instance != null)
+ {
+ return sqlExpressionFactory.Function(
+ "DateTimeAdd",
+ arguments: [sqlExpressionFactory.Constant(datePart), arguments[0], instance],
+ instance.Type,
+ instance.TypeMapping);
+ }
+
+ return null;
+ }
+}
diff --git a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosNullableMemberTranslator.cs b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosNullableMemberTranslator.cs
new file mode 100644
index 00000000000..0e5294664ef
--- /dev/null
+++ b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosNullableMemberTranslator.cs
@@ -0,0 +1,42 @@
+// 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.Cosmos.Query.Internal;
+
+// ReSharper disable once CheckNamespace
+namespace Microsoft.EntityFrameworkCore.Query.Internal;
+
+///
+/// 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.
+///
+public class CosmosNullableMemberTranslator(ISqlExpressionFactory sqlExpressionFactory) : IMemberTranslator
+{
+ ///
+ /// 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.
+ ///
+ public virtual SqlExpression? Translate(
+ SqlExpression? instance,
+ MemberInfo member,
+ Type returnType,
+ IDiagnosticsLogger logger)
+ {
+ if (member.DeclaringType?.IsNullableValueType() == true
+ && instance != null)
+ {
+ return member.Name switch
+ {
+ nameof(Nullable.Value) => instance,
+ nameof(Nullable.HasValue) => sqlExpressionFactory.IsNotNull(instance),
+ _ => null
+ };
+ }
+
+ return null;
+ }
+}
diff --git a/src/EFCore.Relational/Query/Internal/Translators/NullableMemberTranslator.cs b/src/EFCore.Relational/Query/Internal/Translators/NullableMemberTranslator.cs
index 167a17ec546..618d28e347b 100644
--- a/src/EFCore.Relational/Query/Internal/Translators/NullableMemberTranslator.cs
+++ b/src/EFCore.Relational/Query/Internal/Translators/NullableMemberTranslator.cs
@@ -12,21 +12,8 @@ namespace Microsoft.EntityFrameworkCore.Query.Internal;
/// 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.
///
-public class NullableMemberTranslator : IMemberTranslator
+public class NullableMemberTranslator(ISqlExpressionFactory sqlExpressionFactory) : IMemberTranslator
{
- private readonly ISqlExpressionFactory _sqlExpressionFactory;
-
- ///
- /// 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.
- ///
- public NullableMemberTranslator(ISqlExpressionFactory sqlExpressionFactory)
- {
- _sqlExpressionFactory = sqlExpressionFactory;
- }
-
///
/// 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
@@ -42,14 +29,12 @@ public NullableMemberTranslator(ISqlExpressionFactory sqlExpressionFactory)
if (member.DeclaringType?.IsNullableValueType() == true
&& instance != null)
{
- switch (member.Name)
+ return member.Name switch
{
- case nameof(Nullable.Value):
- return instance;
-
- case nameof(Nullable.HasValue):
- return _sqlExpressionFactory.IsNotNull(instance);
- }
+ nameof(Nullable.Value) => instance,
+ nameof(Nullable.HasValue) => sqlExpressionFactory.IsNotNull(instance),
+ _ => null
+ };
}
return null;
diff --git a/src/EFCore.SqlServer/Query/Internal/Translators/SqlServerDateTimeMemberTranslator.cs b/src/EFCore.SqlServer/Query/Internal/Translators/SqlServerDateTimeMemberTranslator.cs
index ff31e7147f4..b2abb4be5cf 100644
--- a/src/EFCore.SqlServer/Query/Internal/Translators/SqlServerDateTimeMemberTranslator.cs
+++ b/src/EFCore.SqlServer/Query/Internal/Translators/SqlServerDateTimeMemberTranslator.cs
@@ -12,38 +12,11 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal;
/// 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.
///
-public class SqlServerDateTimeMemberTranslator : IMemberTranslator
+public class SqlServerDateTimeMemberTranslator(
+ ISqlExpressionFactory sqlExpressionFactory,
+ IRelationalTypeMappingSource typeMappingSource)
+ : IMemberTranslator
{
- private static readonly Dictionary DatePartMapping
- = new()
- {
- { nameof(DateTime.Year), "year" },
- { nameof(DateTime.Month), "month" },
- { nameof(DateTime.DayOfYear), "dayofyear" },
- { nameof(DateTime.Day), "day" },
- { nameof(DateTime.Hour), "hour" },
- { nameof(DateTime.Minute), "minute" },
- { nameof(DateTime.Second), "second" },
- { nameof(DateTime.Millisecond), "millisecond" }
- };
-
- private readonly ISqlExpressionFactory _sqlExpressionFactory;
- private readonly IRelationalTypeMappingSource _typeMappingSource;
-
- ///
- /// 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.
- ///
- public SqlServerDateTimeMemberTranslator(
- ISqlExpressionFactory sqlExpressionFactory,
- IRelationalTypeMappingSource typeMappingSource)
- {
- _sqlExpressionFactory = sqlExpressionFactory;
- _typeMappingSource = typeMappingSource;
- }
-
///
/// 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
@@ -58,81 +31,93 @@ public SqlServerDateTimeMemberTranslator(
{
var declaringType = member.DeclaringType;
- if (declaringType == typeof(DateTime)
- || declaringType == typeof(DateTimeOffset))
+ if (declaringType != typeof(DateTime) && declaringType != typeof(DateTimeOffset))
+ {
+ return null;
+ }
+
+ return member.Name switch
{
- var memberName = member.Name;
+ nameof(DateTime.Year) => DatePart("year"),
+ nameof(DateTime.Month) => DatePart("month"),
+ nameof(DateTime.DayOfYear) => DatePart("dayofyear"),
+ nameof(DateTime.Day) => DatePart("day"),
+ nameof(DateTime.Hour) => DatePart("hour"),
+ nameof(DateTime.Minute) => DatePart("minute"),
+ nameof(DateTime.Second) => DatePart("second"),
+ nameof(DateTime.Millisecond) => DatePart("millisecond"),
- if (DatePartMapping.TryGetValue(memberName, out var datePart))
- {
- return _sqlExpressionFactory.Function(
- "DATEPART",
- new[] { _sqlExpressionFactory.Fragment(datePart), instance! },
+ nameof(DateTime.Date)
+ => sqlExpressionFactory.Function(
+ "CONVERT",
+ new[] { sqlExpressionFactory.Fragment("date"), instance! },
nullable: true,
- argumentsPropagateNullability: new[] { false, true },
- returnType);
- }
+ argumentsPropagateNullability: [false, true],
+ returnType,
+ declaringType == typeof(DateTime)
+ ? instance!.TypeMapping
+ : typeMappingSource.FindMapping(typeof(DateTime))),
- switch (memberName)
- {
- case nameof(DateTime.Date):
- return _sqlExpressionFactory.Function(
- "CONVERT",
- new[] { _sqlExpressionFactory.Fragment("date"), instance! },
- nullable: true,
- argumentsPropagateNullability: new[] { false, true },
- returnType,
- declaringType == typeof(DateTime)
- ? instance!.TypeMapping
- : _typeMappingSource.FindMapping(typeof(DateTime)));
+ nameof(DateTime.TimeOfDay)
+ => sqlExpressionFactory.Function(
+ "CONVERT",
+ new[] { sqlExpressionFactory.Fragment("time"), instance! },
+ nullable: true,
+ argumentsPropagateNullability: [false, true],
+ returnType),
- case nameof(DateTime.TimeOfDay):
- return _sqlExpressionFactory.Function(
- "CONVERT",
- new[] { _sqlExpressionFactory.Fragment("time"), instance! },
- nullable: true,
- argumentsPropagateNullability: new[] { false, true },
- returnType);
+ nameof(DateTime.Now)
+ => sqlExpressionFactory.Function(
+ declaringType == typeof(DateTime) ? "GETDATE" : "SYSDATETIMEOFFSET",
+ arguments: [],
+ nullable: false,
+ argumentsPropagateNullability: [],
+ returnType),
- case nameof(DateTime.Now):
- return _sqlExpressionFactory.Function(
- declaringType == typeof(DateTime) ? "GETDATE" : "SYSDATETIMEOFFSET",
- Enumerable.Empty(),
- nullable: false,
- argumentsPropagateNullability: Enumerable.Empty(),
- returnType);
+ nameof(DateTime.UtcNow)
+ when declaringType == typeof(DateTime)
+ => sqlExpressionFactory.Function(
+ "GETUTCDATE",
+ arguments: [],
+ nullable: false,
+ argumentsPropagateNullability: [],
+ returnType),
- case nameof(DateTime.UtcNow):
- var serverTranslation = _sqlExpressionFactory.Function(
- declaringType == typeof(DateTime) ? "GETUTCDATE" : "SYSUTCDATETIME",
- Enumerable.Empty(),
- nullable: false,
- argumentsPropagateNullability: Enumerable.Empty(),
- returnType);
+ nameof(DateTime.UtcNow)
+ when declaringType == typeof(DateTimeOffset)
+ => sqlExpressionFactory.Convert(sqlExpressionFactory.Function(
+ "SYSUTCDATETIME",
+ arguments: [],
+ nullable: false,
+ argumentsPropagateNullability: [],
+ returnType), returnType),
- return declaringType == typeof(DateTime)
- ? serverTranslation
- : _sqlExpressionFactory.Convert(serverTranslation, returnType);
+ nameof(DateTime.Today)
+ => sqlExpressionFactory.Function(
+ "CONVERT",
+ new SqlExpression[]
+ {
+ sqlExpressionFactory.Fragment("date"),
+ sqlExpressionFactory.Function(
+ "GETDATE",
+ arguments: [],
+ nullable: false,
+ argumentsPropagateNullability: [],
+ typeof(DateTime))
+ },
+ nullable: true,
+ argumentsPropagateNullability: [false, true],
+ returnType),
- case nameof(DateTime.Today):
- return _sqlExpressionFactory.Function(
- "CONVERT",
- new SqlExpression[]
- {
- _sqlExpressionFactory.Fragment("date"),
- _sqlExpressionFactory.Function(
- "GETDATE",
- Enumerable.Empty(),
- nullable: false,
- argumentsPropagateNullability: Enumerable.Empty(),
- typeof(DateTime))
- },
- nullable: true,
- argumentsPropagateNullability: new[] { false, true },
- returnType);
- }
- }
+ _ => null
+ };
- return null;
+ SqlFunctionExpression DatePart(string part)
+ => sqlExpressionFactory.Function(
+ "DATEPART",
+ arguments: [sqlExpressionFactory.Fragment(part), instance!],
+ nullable: true,
+ argumentsPropagateNullability: new[] { false, true },
+ returnType);
}
}
diff --git a/src/EFCore.SqlServer/Query/Internal/Translators/SqlServerDateTimeMethodTranslator.cs b/src/EFCore.SqlServer/Query/Internal/Translators/SqlServerDateTimeMethodTranslator.cs
index c5d977c8727..b38e0261479 100644
--- a/src/EFCore.SqlServer/Query/Internal/Translators/SqlServerDateTimeMethodTranslator.cs
+++ b/src/EFCore.SqlServer/Query/Internal/Translators/SqlServerDateTimeMethodTranslator.cs
@@ -12,9 +12,12 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal;
/// 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.
///
-public class SqlServerDateTimeMethodTranslator : IMethodCallTranslator
+public class SqlServerDateTimeMethodTranslator(
+ ISqlExpressionFactory sqlExpressionFactory,
+ IRelationalTypeMappingSource typeMappingSource)
+ : IMethodCallTranslator
{
- private readonly Dictionary _methodInfoDatePartMapping = new()
+ private static readonly Dictionary MethodInfoDatePartMapping = new()
{
{ typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddYears), [typeof(int)])!, "year" },
{ typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddMonths), [typeof(int)])!, "month" },
@@ -32,7 +35,7 @@ public class SqlServerDateTimeMethodTranslator : IMethodCallTranslator
{ typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.AddMilliseconds), [typeof(double)])!, "millisecond" }
};
- private static readonly Dictionary _methodInfoDateDiffMapping = new()
+ private static readonly Dictionary MethodInfoDateDiffMapping = new()
{
{ typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.ToUnixTimeSeconds), Type.EmptyTypes)!, "second" },
{ typeof(DateTimeOffset).GetRuntimeMethod(nameof(DateTimeOffset.ToUnixTimeMilliseconds), Type.EmptyTypes)!, "millisecond" }
@@ -46,23 +49,6 @@ public class SqlServerDateTimeMethodTranslator : IMethodCallTranslator
.GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.AtTimeZone), [typeof(DbFunctions), typeof(DateTime), typeof(string)])!;
- private readonly ISqlExpressionFactory _sqlExpressionFactory;
- private readonly IRelationalTypeMappingSource _typeMappingSource;
-
- ///
- /// 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.
- ///
- public SqlServerDateTimeMethodTranslator(
- ISqlExpressionFactory sqlExpressionFactory,
- IRelationalTypeMappingSource typeMappingSource)
- {
- _sqlExpressionFactory = sqlExpressionFactory;
- _typeMappingSource = typeMappingSource;
- }
-
///
/// 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
@@ -75,7 +61,7 @@ public SqlServerDateTimeMethodTranslator(
IReadOnlyList arguments,
IDiagnosticsLogger logger)
{
- if (_methodInfoDatePartMapping.TryGetValue(method, out var datePart)
+ if (MethodInfoDatePartMapping.TryGetValue(method, out var datePart)
&& instance != null)
{
// Some Add methods accept a double, and SQL Server DateAdd does not accept number argument outside of int range
@@ -88,14 +74,14 @@ public SqlServerDateTimeMethodTranslator(
// Our default mapping for DateTime is datetime2, so we force constants to be datetime instead here.
if (instance is SqlConstantExpression instanceConstant)
{
- instance = instanceConstant.ApplyTypeMapping(_typeMappingSource.FindMapping(typeof(DateTime), "datetime"));
+ instance = instanceConstant.ApplyTypeMapping(typeMappingSource.FindMapping(typeof(DateTime), "datetime"));
}
- return _sqlExpressionFactory.Function(
+ return sqlExpressionFactory.Function(
"DATEADD",
- new[] { _sqlExpressionFactory.Fragment(datePart), _sqlExpressionFactory.Convert(arguments[0], typeof(int)), instance },
+ arguments: [sqlExpressionFactory.Fragment(datePart), sqlExpressionFactory.Convert(arguments[0], typeof(int)), instance],
nullable: true,
- argumentsPropagateNullability: new[] { false, true, true },
+ argumentsPropagateNullability: [false, true, true],
instance.Type,
instance.TypeMapping);
}
@@ -116,7 +102,7 @@ public SqlServerDateTimeMethodTranslator(
"datetimeoffset"
=> operandTypeMapping,
"datetime" or "datetime2" or "smalldatetime"
- => _typeMappingSource.FindMapping(
+ => typeMappingSource.FindMapping(
typeof(DateTimeOffset), "datetimeoffset", precision: operandTypeMapping.Precision),
_ => null
};
@@ -130,28 +116,28 @@ public SqlServerDateTimeMethodTranslator(
{
// Our constant representation for datetime/datetimeoffset is an untyped string literal, which the AT TIME ZONE expression
// does not accept. Type it explicitly.
- operand = _sqlExpressionFactory.Convert(operand, operand.Type);
+ operand = sqlExpressionFactory.Convert(operand, operand.Type);
}
return new AtTimeZoneExpression(
operand,
- _sqlExpressionFactory.ApplyTypeMapping(timeZone, _typeMappingSource.FindMapping("varchar")),
+ sqlExpressionFactory.ApplyTypeMapping(timeZone, typeMappingSource.FindMapping("varchar")),
typeof(DateTimeOffset),
resultTypeMapping);
}
- if (_methodInfoDateDiffMapping.TryGetValue(method, out var timePart))
+ if (MethodInfoDateDiffMapping.TryGetValue(method, out var timePart))
{
- return _sqlExpressionFactory.Function(
+ return sqlExpressionFactory.Function(
"DATEDIFF_BIG",
- new[]
- {
- _sqlExpressionFactory.Fragment(timePart),
- _sqlExpressionFactory.Constant(DateTimeOffset.UnixEpoch, instance!.TypeMapping),
+ arguments:
+ [
+ sqlExpressionFactory.Fragment(timePart),
+ sqlExpressionFactory.Constant(DateTimeOffset.UnixEpoch, instance!.TypeMapping),
instance
- },
+ ],
nullable: true,
- argumentsPropagateNullability: new[] { false, true, true },
+ argumentsPropagateNullability: [false, true, true],
typeof(long));
}
diff --git a/src/EFCore.Sqlite.Core/Query/Internal/Translators/SqliteDateTimeMemberTranslator.cs b/src/EFCore.Sqlite.Core/Query/Internal/Translators/SqliteDateTimeMemberTranslator.cs
index 008b30360ab..954f1f694e2 100644
--- a/src/EFCore.Sqlite.Core/Query/Internal/Translators/SqliteDateTimeMemberTranslator.cs
+++ b/src/EFCore.Sqlite.Core/Query/Internal/Translators/SqliteDateTimeMemberTranslator.cs
@@ -12,34 +12,8 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Query.Internal;
/// 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.
///
-public class SqliteDateTimeMemberTranslator : IMemberTranslator
+public class SqliteDateTimeMemberTranslator(SqliteSqlExpressionFactory sqlExpressionFactory) : IMemberTranslator
{
- private static readonly Dictionary DatePartMapping
- = new()
- {
- { nameof(DateTime.Year), "%Y" },
- { nameof(DateTime.Month), "%m" },
- { nameof(DateTime.DayOfYear), "%j" },
- { nameof(DateTime.Day), "%d" },
- { nameof(DateTime.Hour), "%H" },
- { nameof(DateTime.Minute), "%M" },
- { nameof(DateTime.Second), "%S" },
- { nameof(DateTime.DayOfWeek), "%w" }
- };
-
- private readonly SqliteSqlExpressionFactory _sqlExpressionFactory;
-
- ///
- /// 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.
- ///
- public SqliteDateTimeMemberTranslator(SqliteSqlExpressionFactory sqlExpressionFactory)
- {
- _sqlExpressionFactory = sqlExpressionFactory;
- }
-
///
/// 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
@@ -52,112 +26,123 @@ public SqliteDateTimeMemberTranslator(SqliteSqlExpressionFactory sqlExpressionFa
Type returnType,
IDiagnosticsLogger logger)
{
- if (member.DeclaringType == typeof(DateTime))
+ if (member.DeclaringType != typeof(DateTime))
{
- var memberName = member.Name;
+ return null;
+ }
- if (DatePartMapping.TryGetValue(memberName, out var datePart))
- {
- return _sqlExpressionFactory.Convert(
- _sqlExpressionFactory.Strftime(
- typeof(string),
- datePart,
- instance!),
- returnType);
- }
-
- if (memberName == nameof(DateTime.Ticks))
- {
- return _sqlExpressionFactory.Convert(
- _sqlExpressionFactory.Multiply(
- _sqlExpressionFactory.Subtract(
- _sqlExpressionFactory.Function(
+ var memberName = member.Name;
+
+ switch (memberName)
+ {
+ case nameof(DateTime.Year):
+ return DatePart("%Y");
+ case nameof(DateTime.Month):
+ return DatePart("%m");
+ case nameof(DateTime.DayOfYear):
+ return DatePart("%j");
+ case nameof(DateTime.Day):
+ return DatePart("%d");
+ case nameof(DateTime.Hour):
+ return DatePart("%H");
+ case nameof(DateTime.Minute):
+ return DatePart("%M");
+ case nameof(DateTime.Second):
+ return DatePart("%S");
+ case nameof(DateTime.DayOfWeek):
+ return DatePart("%w");
+
+ case nameof(DateTime.Ticks):
+ return sqlExpressionFactory.Convert(
+ sqlExpressionFactory.Multiply(
+ sqlExpressionFactory.Subtract(
+ sqlExpressionFactory.Function(
"julianday",
new[] { instance! },
nullable: true,
argumentsPropagateNullability: new[] { true },
typeof(double)),
- _sqlExpressionFactory.Constant(1721425.5)), // NB: Result of julianday('0001-01-01 00:00:00')
- _sqlExpressionFactory.Constant(TimeSpan.TicksPerDay)),
+ sqlExpressionFactory.Constant(1721425.5)), // NB: Result of julianday('0001-01-01 00:00:00')
+ sqlExpressionFactory.Constant(TimeSpan.TicksPerDay)),
typeof(long));
- }
- if (memberName == nameof(DateTime.Millisecond))
- {
- return _sqlExpressionFactory.Modulo(
- _sqlExpressionFactory.Multiply(
- _sqlExpressionFactory.Convert(
- _sqlExpressionFactory.Strftime(
+ case nameof(DateTime.Millisecond):
+ return sqlExpressionFactory.Modulo(
+ sqlExpressionFactory.Multiply(
+ sqlExpressionFactory.Convert(
+ sqlExpressionFactory.Strftime(
typeof(string),
"%f",
instance!),
typeof(double)),
- _sqlExpressionFactory.Constant(1000)),
- _sqlExpressionFactory.Constant(1000));
- }
+ sqlExpressionFactory.Constant(1000)),
+ sqlExpressionFactory.Constant(1000));
+ }
- var format = "%Y-%m-%d %H:%M:%f";
- SqlExpression timestring;
- var modifiers = new List();
+ var format = "%Y-%m-%d %H:%M:%f";
+ SqlExpression timestring;
+ var modifiers = new List();
- switch (memberName)
- {
- case nameof(DateTime.Now):
- timestring = _sqlExpressionFactory.Constant("now");
- modifiers.Add(_sqlExpressionFactory.Constant("localtime"));
- break;
-
- case nameof(DateTime.UtcNow):
- timestring = _sqlExpressionFactory.Constant("now");
- break;
-
- case nameof(DateTime.Date):
- timestring = instance!;
- modifiers.Add(_sqlExpressionFactory.Constant("start of day"));
- break;
-
- case nameof(DateTime.Today):
- timestring = _sqlExpressionFactory.Constant("now");
- modifiers.Add(_sqlExpressionFactory.Constant("localtime"));
- modifiers.Add(_sqlExpressionFactory.Constant("start of day"));
- break;
-
- case nameof(DateTime.TimeOfDay):
- format = "%H:%M:%f";
- timestring = instance!;
- break;
-
- default:
- return null;
- }
-
- Check.DebugAssert(timestring != null, "timestring is null");
-
- return _sqlExpressionFactory.Function(
- "rtrim",
- new SqlExpression[]
- {
- _sqlExpressionFactory.Function(
- "rtrim",
- new SqlExpression[]
- {
- _sqlExpressionFactory.Strftime(
- returnType,
- format,
- timestring,
- modifiers),
- _sqlExpressionFactory.Constant("0")
- },
- nullable: true,
- argumentsPropagateNullability: new[] { true, false },
- returnType),
- _sqlExpressionFactory.Constant(".")
- },
- nullable: true,
- argumentsPropagateNullability: new[] { true, false },
- returnType);
+ switch (memberName)
+ {
+ case nameof(DateTime.Now):
+ timestring = sqlExpressionFactory.Constant("now");
+ modifiers.Add(sqlExpressionFactory.Constant("localtime"));
+ break;
+
+ case nameof(DateTime.UtcNow):
+ timestring = sqlExpressionFactory.Constant("now");
+ break;
+
+ case nameof(DateTime.Date):
+ timestring = instance!;
+ modifiers.Add(sqlExpressionFactory.Constant("start of day"));
+ break;
+
+ case nameof(DateTime.Today):
+ timestring = sqlExpressionFactory.Constant("now");
+ modifiers.Add(sqlExpressionFactory.Constant("localtime"));
+ modifiers.Add(sqlExpressionFactory.Constant("start of day"));
+ break;
+
+ case nameof(DateTime.TimeOfDay):
+ format = "%H:%M:%f";
+ timestring = instance!;
+ break;
+
+ default:
+ return null;
}
- return null;
+ Check.DebugAssert(timestring != null, "timestring is null");
+
+ return sqlExpressionFactory.Function(
+ "rtrim",
+ new SqlExpression[]
+ {
+ sqlExpressionFactory.Function(
+ "rtrim",
+ new SqlExpression[]
+ {
+ sqlExpressionFactory.Strftime(
+ returnType,
+ format,
+ timestring,
+ modifiers),
+ sqlExpressionFactory.Constant("0")
+ },
+ nullable: true,
+ argumentsPropagateNullability: new[] { true, false },
+ returnType),
+ sqlExpressionFactory.Constant(".")
+ },
+ nullable: true,
+ argumentsPropagateNullability: new[] { true, false },
+ returnType);
+
+ SqlExpression DatePart(string part)
+ => sqlExpressionFactory.Convert(
+ sqlExpressionFactory.Strftime(typeof(string), part, instance!),
+ returnType);
}
}
diff --git a/src/EFCore.Sqlite.Core/Query/Internal/Translators/SqliteDateTimeMethodTranslator.cs b/src/EFCore.Sqlite.Core/Query/Internal/Translators/SqliteDateTimeMethodTranslator.cs
index 09b6cef56d9..a547abab5cc 100644
--- a/src/EFCore.Sqlite.Core/Query/Internal/Translators/SqliteDateTimeMethodTranslator.cs
+++ b/src/EFCore.Sqlite.Core/Query/Internal/Translators/SqliteDateTimeMethodTranslator.cs
@@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Query.Internal;
/// 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.
///
-public class SqliteDateTimeMethodTranslator : IMethodCallTranslator
+public class SqliteDateTimeMethodTranslator(SqliteSqlExpressionFactory sqlExpressionFactory) : IMethodCallTranslator
{
private static readonly MethodInfo AddMilliseconds
= typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddMilliseconds), [typeof(double)])!;
@@ -20,7 +20,7 @@ private static readonly MethodInfo AddMilliseconds
private static readonly MethodInfo AddTicks
= typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddTicks), [typeof(long)])!;
- private readonly Dictionary _methodInfoToUnitSuffix = new()
+ private static readonly Dictionary MethodInfoToUnitSuffix = new()
{
{ typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddYears), [typeof(int)])!, " years" },
{ typeof(DateTime).GetRuntimeMethod(nameof(DateTime.AddMonths), [typeof(int)])!, " months" },
@@ -33,19 +33,6 @@ private static readonly MethodInfo AddTicks
{ typeof(DateOnly).GetRuntimeMethod(nameof(DateOnly.AddDays), [typeof(int)])!, " days" }
};
- private readonly SqliteSqlExpressionFactory _sqlExpressionFactory;
-
- ///
- /// 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.
- ///
- public SqliteDateTimeMethodTranslator(SqliteSqlExpressionFactory sqlExpressionFactory)
- {
- _sqlExpressionFactory = sqlExpressionFactory;
- }
-
///
/// 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
@@ -71,55 +58,55 @@ public SqliteDateTimeMethodTranslator(SqliteSqlExpressionFactory sqlExpressionFa
SqlExpression? modifier = null;
if (AddMilliseconds.Equals(method))
{
- modifier = _sqlExpressionFactory.Add(
- _sqlExpressionFactory.Convert(
- _sqlExpressionFactory.Divide(
+ modifier = sqlExpressionFactory.Add(
+ sqlExpressionFactory.Convert(
+ sqlExpressionFactory.Divide(
arguments[0],
- _sqlExpressionFactory.Constant(1000.0)),
+ sqlExpressionFactory.Constant(1000.0)),
typeof(string)),
- _sqlExpressionFactory.Constant(" seconds"));
+ sqlExpressionFactory.Constant(" seconds"));
}
else if (AddTicks.Equals(method))
{
- modifier = _sqlExpressionFactory.Add(
- _sqlExpressionFactory.Convert(
- _sqlExpressionFactory.Divide(
+ modifier = sqlExpressionFactory.Add(
+ sqlExpressionFactory.Convert(
+ sqlExpressionFactory.Divide(
arguments[0],
- _sqlExpressionFactory.Constant((double)TimeSpan.TicksPerSecond)),
+ sqlExpressionFactory.Constant((double)TimeSpan.TicksPerSecond)),
typeof(string)),
- _sqlExpressionFactory.Constant(" seconds"));
+ sqlExpressionFactory.Constant(" seconds"));
}
- else if (_methodInfoToUnitSuffix.TryGetValue(method, out var unitSuffix))
+ else if (MethodInfoToUnitSuffix.TryGetValue(method, out var unitSuffix))
{
- modifier = _sqlExpressionFactory.Add(
- _sqlExpressionFactory.Convert(arguments[0], typeof(string)),
- _sqlExpressionFactory.Constant(unitSuffix));
+ modifier = sqlExpressionFactory.Add(
+ sqlExpressionFactory.Convert(arguments[0], typeof(string)),
+ sqlExpressionFactory.Constant(unitSuffix));
}
if (modifier != null)
{
- return _sqlExpressionFactory.Function(
+ return sqlExpressionFactory.Function(
"rtrim",
new SqlExpression[]
{
- _sqlExpressionFactory.Function(
+ sqlExpressionFactory.Function(
"rtrim",
new SqlExpression[]
{
- _sqlExpressionFactory.Strftime(
+ sqlExpressionFactory.Strftime(
method.ReturnType,
"%Y-%m-%d %H:%M:%f",
instance!,
- new[] { modifier }),
- _sqlExpressionFactory.Constant("0")
+ modifiers: [modifier]),
+ sqlExpressionFactory.Constant("0")
},
nullable: true,
- argumentsPropagateNullability: new[] { true, false },
+ argumentsPropagateNullability: [true, false],
method.ReturnType),
- _sqlExpressionFactory.Constant(".")
+ sqlExpressionFactory.Constant(".")
},
nullable: true,
- argumentsPropagateNullability: new[] { true, false },
+ argumentsPropagateNullability: [true, false],
method.ReturnType);
}
@@ -131,17 +118,17 @@ public SqliteDateTimeMethodTranslator(SqliteSqlExpressionFactory sqlExpressionFa
MethodInfo method,
IReadOnlyList arguments)
{
- if (instance is not null && _methodInfoToUnitSuffix.TryGetValue(method, out var unitSuffix))
+ if (instance is not null && MethodInfoToUnitSuffix.TryGetValue(method, out var unitSuffix))
{
- return _sqlExpressionFactory.Date(
+ return sqlExpressionFactory.Date(
method.ReturnType,
instance,
- new[]
- {
- _sqlExpressionFactory.Add(
- _sqlExpressionFactory.Convert(arguments[0], typeof(string)),
- _sqlExpressionFactory.Constant(unitSuffix))
- });
+ modifiers:
+ [
+ sqlExpressionFactory.Add(
+ sqlExpressionFactory.Convert(arguments[0], typeof(string)),
+ sqlExpressionFactory.Constant(unitSuffix))
+ ]);
}
return null;
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs
index f57b1a24b00..b97f09b9848 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs
@@ -2423,29 +2423,77 @@ public override async Task Handle_materialization_properly_when_more_than_two_qu
AssertSql();
}
- public override async Task Parameter_extraction_short_circuits_1(bool async)
- {
- // Optimize query SQL. Issue #13159.
- await AssertTranslationFailed(() => base.Parameter_extraction_short_circuits_1(async));
+ public override Task Parameter_extraction_short_circuits_1(bool async)
+ => Fixture.NoSyncTest(
+ async, async a =>
+ {
+ await base.Parameter_extraction_short_circuits_1(a);
- AssertSql();
- }
+ // Optimize query SQL. Issue #13159.
+ AssertSql(
+ """
+@__dateFilter_Value_Month_0='7'
+@__dateFilter_Value_Year_1='1996'
- public override async Task Parameter_extraction_short_circuits_2(bool async)
- {
- // Cosmos client evaluation. Issue #17246.
- await AssertTranslationFailed(() => base.Parameter_extraction_short_circuits_2(async));
+SELECT c
+FROM root c
+WHERE ((c["Discriminator"] = "Order") AND ((c["OrderID"] < 10400) AND (false OR (((c["OrderDate"] != null) AND (DateTimePart("mm", c["OrderDate"]) = @__dateFilter_Value_Month_0)) AND (DateTimePart("yyyy", c["OrderDate"]) = @__dateFilter_Value_Year_1)))))
+""",
+ //
+ """
+SELECT c
+FROM root c
+WHERE ((c["Discriminator"] = "Order") AND (c["OrderID"] < 10400))
+""");
+ });
- AssertSql();
- }
+ public override Task Parameter_extraction_short_circuits_2(bool async)
+ => Fixture.NoSyncTest(
+ async, async a =>
+ {
+ await base.Parameter_extraction_short_circuits_2(a);
- public override async Task Parameter_extraction_short_circuits_3(bool async)
- {
- // Optimize query SQL. Issue #13159.
- await AssertTranslationFailed(() => base.Parameter_extraction_short_circuits_3(async));
+ // Optimize query SQL. Issue #13159.
+ AssertSql(
+ """
+@__dateFilter_Value_Month_0='7'
+@__dateFilter_Value_Year_1='1996'
- AssertSql();
- }
+SELECT c
+FROM root c
+WHERE ((c["Discriminator"] = "Order") AND ((c["OrderID"] < 10400) AND (((c["OrderDate"] != null) AND (DateTimePart("mm", c["OrderDate"]) = @__dateFilter_Value_Month_0)) AND (DateTimePart("yyyy", c["OrderDate"]) = @__dateFilter_Value_Year_1))))
+""",
+ //
+ """
+SELECT c
+FROM root c
+WHERE ((c["Discriminator"] = "Order") AND false)
+""");
+ });
+
+ public override Task Parameter_extraction_short_circuits_3(bool async)
+ => Fixture.NoSyncTest(
+ async, async a =>
+ {
+ await base.Parameter_extraction_short_circuits_3(a);
+
+ // Optimize query SQL. Issue #13159.
+ AssertSql(
+ """
+@__dateFilter_Value_Month_0='7'
+@__dateFilter_Value_Year_1='1996'
+
+SELECT c
+FROM root c
+WHERE ((c["Discriminator"] = "Order") AND ((c["OrderID"] < 10400) OR (((c["OrderDate"] != null) AND (DateTimePart("mm", c["OrderDate"]) = @__dateFilter_Value_Month_0)) AND (DateTimePart("yyyy", c["OrderDate"]) = @__dateFilter_Value_Year_1))))
+""",
+ //
+ """
+SELECT c
+FROM root c
+WHERE (c["Discriminator"] = "Order")
+""");
+ });
public override async Task Subquery_member_pushdown_does_not_change_original_subquery_model(bool async)
{
@@ -2533,7 +2581,7 @@ public override Task Select_expression_date_add_year(bool async)
AssertSql(
"""
-SELECT c["OrderDate"]
+SELECT DateTimeAdd("yyyy", 1, c["OrderDate"]) AS c
FROM root c
WHERE ((c["Discriminator"] = "Order") AND (c["OrderDate"] != null))
""");
@@ -2547,7 +2595,7 @@ public override Task Select_expression_datetime_add_month(bool async)
AssertSql(
"""
-SELECT c["OrderDate"]
+SELECT DateTimeAdd("mm", 1, c["OrderDate"]) AS c
FROM root c
WHERE ((c["Discriminator"] = "Order") AND (c["OrderDate"] != null))
""");
@@ -2561,7 +2609,7 @@ public override Task Select_expression_datetime_add_hour(bool async)
AssertSql(
"""
-SELECT c["OrderDate"]
+SELECT DateTimeAdd("hh", 1.0, c["OrderDate"]) AS c
FROM root c
WHERE ((c["Discriminator"] = "Order") AND (c["OrderDate"] != null))
""");
@@ -2575,7 +2623,7 @@ public override Task Select_expression_datetime_add_minute(bool async)
AssertSql(
"""
-SELECT c["OrderDate"]
+SELECT DateTimeAdd("mi", 1.0, c["OrderDate"]) AS c
FROM root c
WHERE ((c["Discriminator"] = "Order") AND (c["OrderDate"] != null))
""");
@@ -2589,7 +2637,7 @@ public override Task Select_expression_datetime_add_second(bool async)
AssertSql(
"""
-SELECT c["OrderDate"]
+SELECT DateTimeAdd("ss", 1.0, c["OrderDate"]) AS c
FROM root c
WHERE ((c["Discriminator"] = "Order") AND (c["OrderDate"] != null))
""");
@@ -2603,7 +2651,7 @@ public override Task Select_expression_date_add_milliseconds_above_the_range(boo
AssertSql(
"""
-SELECT c["OrderDate"]
+SELECT DateTimeAdd("ms", 1000000000000.0, c["OrderDate"]) AS c
FROM root c
WHERE ((c["Discriminator"] = "Order") AND (c["OrderDate"] != null))
""");
@@ -2617,7 +2665,7 @@ public override Task Select_expression_date_add_milliseconds_below_the_range(boo
AssertSql(
"""
-SELECT c["OrderDate"]
+SELECT DateTimeAdd("ms", -1000000000000.0, c["OrderDate"]) AS c
FROM root c
WHERE ((c["Discriminator"] = "Order") AND (c["OrderDate"] != null))
""");
@@ -2631,7 +2679,7 @@ public override Task Select_expression_date_add_milliseconds_large_number_divide
AssertSql(
"""
-SELECT c["OrderDate"]
+SELECT c["OrderDate"], DateTimePart("ms", c["OrderDate"]) AS c
FROM root c
WHERE ((c["Discriminator"] = "Order") AND (c["OrderDate"] != null))
""");
@@ -2652,13 +2700,21 @@ ORDER BY c["OrderID"]
""");
});
- public override async Task Select_expression_references_are_updated_correctly_with_subquery(bool async)
- {
- // Cosmos client evaluation. Issue #17246.
- await AssertTranslationFailed(() => base.Select_expression_references_are_updated_correctly_with_subquery(async));
+ public override Task Select_expression_references_are_updated_correctly_with_subquery(bool async)
+ => Fixture.NoSyncTest(
+ async, async a =>
+ {
+ await base.Select_expression_references_are_updated_correctly_with_subquery(a);
- AssertSql();
- }
+ AssertSql(
+ """
+@__nextYear_0='2017'
+
+SELECT DISTINCT DateTimePart("yyyy", c["OrderDate"]) AS c
+FROM root c
+WHERE (((c["Discriminator"] = "Order") AND (c["OrderDate"] != null)) AND (DateTimePart("yyyy", c["OrderDate"]) < @__nextYear_0))
+""");
+ });
public override async Task DefaultIfEmpty_without_group_join(bool async)
{
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindSelectQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindSelectQueryCosmosTest.cs
index 0de240b54cc..db461fc5360 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindSelectQueryCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindSelectQueryCosmosTest.cs
@@ -712,18 +712,7 @@ public override async Task Projection_in_a_subquery_should_be_liftable(bool asyn
}
public override Task Projection_containing_DateTime_subtraction(bool async)
- => Fixture.NoSyncTest(
- async, async a =>
- {
- await base.Projection_containing_DateTime_subtraction(a);
-
- AssertSql(
- """
-SELECT c["OrderDate"]
-FROM root c
-WHERE ((c["Discriminator"] = "Order") AND (c["OrderID"] < 10300))
-""");
- });
+ => Assert.ThrowsAsync(() => base.Projection_containing_DateTime_subtraction(async));
public override async Task Project_single_element_from_collection_with_OrderBy_Take_and_FirstOrDefault(bool async)
{
@@ -824,7 +813,7 @@ public override Task Select_datetime_year_component(bool async)
AssertSql(
"""
-SELECT c["OrderDate"]
+SELECT DateTimePart("yyyy", c["OrderDate"]) AS c
FROM root c
WHERE (c["Discriminator"] = "Order")
""");
@@ -838,7 +827,7 @@ public override Task Select_datetime_month_component(bool async)
AssertSql(
"""
-SELECT c["OrderDate"]
+SELECT DateTimePart("mm", c["OrderDate"]) AS c
FROM root c
WHERE (c["Discriminator"] = "Order")
""");
@@ -848,6 +837,7 @@ public override Task Select_datetime_day_of_year_component(bool async)
=> Fixture.NoSyncTest(
async, async a =>
{
+ // DateTime.DayOfYear not supported by Cosmos
await base.Select_datetime_day_of_year_component(a);
AssertSql(
@@ -866,7 +856,7 @@ public override Task Select_datetime_day_component(bool async)
AssertSql(
"""
-SELECT c["OrderDate"]
+SELECT DateTimePart("dd", c["OrderDate"]) AS c
FROM root c
WHERE (c["Discriminator"] = "Order")
""");
@@ -880,7 +870,7 @@ public override Task Select_datetime_hour_component(bool async)
AssertSql(
"""
-SELECT c["OrderDate"]
+SELECT DateTimePart("hh", c["OrderDate"]) AS c
FROM root c
WHERE (c["Discriminator"] = "Order")
""");
@@ -894,7 +884,7 @@ public override Task Select_datetime_minute_component(bool async)
AssertSql(
"""
-SELECT c["OrderDate"]
+SELECT DateTimePart("mi", c["OrderDate"]) AS c
FROM root c
WHERE (c["Discriminator"] = "Order")
""");
@@ -908,7 +898,7 @@ public override Task Select_datetime_second_component(bool async)
AssertSql(
"""
-SELECT c["OrderDate"]
+SELECT DateTimePart("ss", c["OrderDate"]) AS c
FROM root c
WHERE (c["Discriminator"] = "Order")
""");
@@ -922,7 +912,7 @@ public override Task Select_datetime_millisecond_component(bool async)
AssertSql(
"""
-SELECT c["OrderDate"]
+SELECT DateTimePart("ms", c["OrderDate"]) AS c
FROM root c
WHERE (c["Discriminator"] = "Order")
""");
@@ -1543,8 +1533,10 @@ public override Task Ternary_in_client_eval_assigns_correct_types(bool async)
SELECT VALUE
{
"CustomerID" : c["CustomerID"],
+ "c" : (c["OrderDate"] != null),
"OrderDate" : c["OrderDate"],
- "c" : (c["OrderID"] - 10000)
+ "c0" : (c["OrderID"] - 10000),
+ "c1" : ((c["OrderDate"] != null) = false)
}
FROM root c
WHERE ((c["Discriminator"] = "Order") AND (c["OrderID"] < 10300))
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs
index 7b339e1b598..9d7781da38b 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs
@@ -1293,77 +1293,125 @@ public override async Task Where_datetime_date_component(bool async)
AssertSql();
}
- public override async Task Where_date_add_year_constant_component(bool async)
- {
- // Cosmos client evaluation. Issue #17246.
- await AssertTranslationFailed(() => base.Where_date_add_year_constant_component(async));
+ public override Task Where_date_add_year_constant_component(bool async)
+ => Fixture.NoSyncTest(
+ async, async a =>
+ {
+ await base.Where_date_add_year_constant_component(a);
- AssertSql();
- }
+ AssertSql(
+ """
+SELECT c
+FROM root c
+WHERE ((c["Discriminator"] = "Order") AND (DateTimePart("yyyy", DateTimeAdd("yyyy", -1, c["OrderDate"])) = 1997))
+""");
+ });
- public override async Task Where_datetime_year_component(bool async)
- {
- // Cosmos client evaluation. Issue #17246.
- await AssertTranslationFailed(() => base.Where_datetime_year_component(async));
+ public override Task Where_datetime_year_component(bool async)
+ => Fixture.NoSyncTest(
+ async, async a =>
+ {
+ await base.Where_datetime_year_component(a);
- AssertSql();
- }
+ AssertSql(
+ """
+SELECT c
+FROM root c
+WHERE ((c["Discriminator"] = "Order") AND (DateTimePart("yyyy", c["OrderDate"]) = 1998))
+""");
+ });
- public override async Task Where_datetime_month_component(bool async)
- {
- // Cosmos client evaluation. Issue #17246.
- await AssertTranslationFailed(() => base.Where_datetime_month_component(async));
+ public override Task Where_datetime_month_component(bool async)
+ => Fixture.NoSyncTest(
+ async, async a =>
+ {
+ await base.Where_datetime_month_component(a);
- AssertSql();
- }
+ AssertSql(
+ """
+SELECT c
+FROM root c
+WHERE ((c["Discriminator"] = "Order") AND (DateTimePart("mm", c["OrderDate"]) = 4))
+""");
+ });
public override async Task Where_datetime_dayOfYear_component(bool async)
{
- // Cosmos client evaluation. Issue #17246.
+ // DateTime.DayOfYear not supported by Cosmos
await AssertTranslationFailed(() => base.Where_datetime_dayOfYear_component(async));
AssertSql();
}
- public override async Task Where_datetime_day_component(bool async)
- {
- // Cosmos client evaluation. Issue #17246.
- await AssertTranslationFailed(() => base.Where_datetime_day_component(async));
+ public override Task Where_datetime_day_component(bool async)
+ => Fixture.NoSyncTest(
+ async, async a =>
+ {
+ await base.Where_datetime_day_component(a);
- AssertSql();
- }
+ AssertSql(
+ """
+SELECT c
+FROM root c
+WHERE ((c["Discriminator"] = "Order") AND (DateTimePart("dd", c["OrderDate"]) = 4))
+""");
+ });
- public override async Task Where_datetime_hour_component(bool async)
- {
- // Cosmos client evaluation. Issue #17246.
- await AssertTranslationFailed(() => base.Where_datetime_hour_component(async));
+ public override Task Where_datetime_hour_component(bool async)
+ => Fixture.NoSyncTest(
+ async, async a =>
+ {
+ await base.Where_datetime_hour_component(a);
- AssertSql();
- }
+ AssertSql(
+ """
+SELECT c
+FROM root c
+WHERE ((c["Discriminator"] = "Order") AND (DateTimePart("hh", c["OrderDate"]) = 0))
+""");
+ });
- public override async Task Where_datetime_minute_component(bool async)
- {
- // Cosmos client evaluation. Issue #17246.
- await AssertTranslationFailed(() => base.Where_datetime_minute_component(async));
+ public override Task Where_datetime_minute_component(bool async)
+ => Fixture.NoSyncTest(
+ async, async a =>
+ {
+ await base.Where_datetime_minute_component(a);
- AssertSql();
- }
+ AssertSql(
+ """
+SELECT c
+FROM root c
+WHERE ((c["Discriminator"] = "Order") AND (DateTimePart("mi", c["OrderDate"]) = 0))
+""");
+ });
- public override async Task Where_datetime_second_component(bool async)
- {
- // Cosmos client evaluation. Issue #17246.
- await AssertTranslationFailed(() => base.Where_datetime_second_component(async));
+ public override Task Where_datetime_second_component(bool async)
+ => Fixture.NoSyncTest(
+ async, async a =>
+ {
+ await base.Where_datetime_second_component(a);
- AssertSql();
- }
+ AssertSql(
+ """
+SELECT c
+FROM root c
+WHERE ((c["Discriminator"] = "Order") AND (DateTimePart("ss", c["OrderDate"]) = 0))
+""");
+ });
- public override async Task Where_datetime_millisecond_component(bool async)
- {
- // Cosmos client evaluation. Issue #17246.
- await AssertTranslationFailed(() => base.Where_datetime_millisecond_component(async));
+ public override Task Where_datetime_millisecond_component(bool async)
+ => Fixture.NoSyncTest(
+ async, async a =>
+ {
+ await base.Where_datetime_millisecond_component(a);
- AssertSql();
- }
+ AssertSql(
+ """
+SELECT c
+FROM root c
+WHERE ((c["Discriminator"] = "Order") AND (DateTimePart("ms", c["OrderDate"]) = 0))
+""");
+ });
public override async Task Where_datetimeoffset_now_component(bool async)
{
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs
index b1f35960b9c..cf3f7aaa11c 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs
@@ -1566,7 +1566,7 @@ public override async Task Project_collection_of_datetimes_filtered(bool async)
// Always throws for sync.
if (async)
{
- await Assert.ThrowsAsync(() => base.Project_collection_of_datetimes_filtered(async));
+ await Assert.ThrowsAsync(() => base.Project_collection_of_datetimes_filtered(async));
}
}
@@ -1656,8 +1656,32 @@ public override async Task Project_multiple_collections(bool async)
// Always throws for sync.
if (async)
{
- // TODO: Project out primitive collection subquery: #33797
- await Assert.ThrowsAsync(() => base.Project_multiple_collections(async));
+ var exception = await Assert.ThrowsAsync(() => base.Project_multiple_collections(async));
+
+ Assert.Contains("'ORDER BY' is not supported in subqueries.", exception.Message);
+
+ AssertSql(
+ """
+SELECT VALUE
+{
+ "Ints" : c["Ints"],
+ "c" : ARRAY(
+ SELECT VALUE i
+ FROM i IN c["Ints"]
+ ORDER BY i DESC),
+ "c0" : ARRAY(
+ SELECT VALUE i
+ FROM i IN c["DateTimes"]
+ WHERE (DateTimePart("dd", i) != 1)),
+ "c1" : ARRAY(
+ SELECT VALUE i
+ FROM i IN c["DateTimes"]
+ WHERE (i > "2000-01-01T00:00:00"))
+}
+FROM root c
+WHERE (c["Discriminator"] = "PrimitiveCollectionsEntity")
+ORDER BY c["Id"]
+""");
}
}