Skip to content

Commit 136c4fc

Browse files
committed
Update to EF10 RTM
1 parent fc07b9b commit 136c4fc

File tree

87 files changed

+12007
-1109
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+12007
-1109
lines changed

Dependencies.targets

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<Project>
22
<PropertyGroup>
3-
<DotNetVersion>[10.0.0-rc.2.25502.107,10.0.999]</DotNetVersion>
4-
<EFCoreVersion>[10.0.0-rc.2.25502.107,10.0.999]</EFCoreVersion>
5-
<MSLibVersion>[10.0.0-rc.2.25502.107,10.0.999]</MSLibVersion>
3+
<DotNetVersion>[10.0.0,10.0.999]</DotNetVersion>
4+
<EFCoreVersion>[10.0.0,10.0.999]</EFCoreVersion>
5+
<MSLibVersion>[10.0.0,10.0.999]</MSLibVersion>
66
</PropertyGroup>
77

88
<ItemGroup>
@@ -28,9 +28,9 @@
2828
<PackageReference Update="Microsoft.EntityFrameworkCore.Design" Version="$(EFCoreVersion)" />
2929
<PackageReference Update="Microsoft.EntityFrameworkCore.Relational.Specification.Tests" Version="$(EFCoreVersion)" />
3030
<PackageReference Update="Microsoft.Extensions.Logging.Console" Version="$(MSLibVersion)" />
31-
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="18.0.0" />
32-
<PackageReference Update="MSTest.TestAdapter" Version="4.0.1" />
33-
<PackageReference Update="MSTest.TestFramework" Version="4.0.1" />
31+
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="18.0.1" />
32+
<PackageReference Update="MSTest.TestAdapter" Version="4.0.2" />
33+
<PackageReference Update="MSTest.TestFramework" Version="4.0.2" />
3434
<PackageReference Update="coverlet.collector" Version="6.0.4" />
3535
<PackageReference Update="Newtonsoft.Json" Version="13.0.4" />
3636

global.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"sdk": {
3-
"version": "10.0.100-rc.1",
3+
"version": "10.0.100",
44
"allowPrerelease": true,
55
"rollForward": "latestFeature"
66
}

src/EFCore.Jet/Migrations/Internal/JetMigrationCommandExecutor.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,8 @@ public override int ExecuteNonQuery(
2727
output += base.ExecuteNonQuery(batch.Item1, connection, executionState, true, isolationLevel);
2828
if (batch.Item2) Thread.Sleep(4000);//Wait for adox/dao to complete
2929
}
30-
if (connection.CurrentTransaction != null)
31-
{
32-
connection.CurrentTransaction.Commit();
33-
}
30+
31+
connection.CurrentTransaction?.Commit();
3432
return output;
3533
}
3634

src/EFCore.Jet/Query/ExpressionTranslators/Internal/JetConvertTranslator.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ public class JetConvertTranslator(ISqlExpressionFactory sqlExpressionFactory) :
3737
typeof(int),
3838
typeof(long),
3939
typeof(short),
40-
typeof(string)
40+
typeof(string),
41+
typeof(object)
4142
];
4243

4344
private static readonly IEnumerable<MethodInfo> _supportedMethods

src/EFCore.Jet/Query/ExpressionTranslators/Internal/JetDateOnlyMemberTranslator.cs

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,6 @@ namespace EntityFrameworkCore.Jet.Query.ExpressionTranslators.Internal;
1919
/// </remarks>
2020
public class JetDateOnlyMemberTranslator(ISqlExpressionFactory sqlExpressionFactory) : IMemberTranslator
2121
{
22-
private static readonly Dictionary<string, string> DatePartMapping
23-
= new()
24-
{
25-
{ nameof(DateTime.Year), "yyyy" },
26-
{ nameof(DateTime.Month), "m" },
27-
{ nameof(DateTime.DayOfYear), "y" },
28-
{ nameof(DateTime.Day), "d" }
29-
};
30-
31-
private readonly ISqlExpressionFactory _sqlExpressionFactory = sqlExpressionFactory;
32-
3322
/// <summary>
3423
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
3524
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -41,12 +30,39 @@ private static readonly Dictionary<string, string> DatePartMapping
4130
MemberInfo member,
4231
Type returnType,
4332
IDiagnosticsLogger<DbLoggerCategory.Query> logger)
44-
=> member.DeclaringType == typeof(DateOnly) && DatePartMapping.TryGetValue(member.Name, out var datePart)
45-
? _sqlExpressionFactory.Function(
33+
{
34+
if (member.DeclaringType != typeof(DateOnly) || instance is null)
35+
{
36+
return null;
37+
}
38+
39+
return member.Name switch
40+
{
41+
nameof(DateOnly.Year) => DatePart("yyyy"),
42+
nameof(DateOnly.Month) => DatePart("m"),
43+
nameof(DateOnly.DayOfYear) => DatePart("y"),
44+
nameof(DateOnly.Day) => DatePart("d"),
45+
46+
nameof(DateOnly.DayNumber) => sqlExpressionFactory.Add(sqlExpressionFactory.Function(
47+
"DATEDIFF",
48+
[
49+
sqlExpressionFactory.Constant("d"),
50+
sqlExpressionFactory.Constant(new DateOnly(100, 1, 1)),
51+
instance
52+
],
53+
nullable: true,
54+
argumentsPropagateNullability: [false, true, true],
55+
returnType),sqlExpressionFactory.Constant(new DateOnly(100,1,1).DayNumber)),
56+
57+
_ => null
58+
};
59+
60+
SqlExpression DatePart(string datePart)
61+
=> sqlExpressionFactory.Function(
4662
"DATEPART",
47-
[_sqlExpressionFactory.Constant(datePart), instance!],
63+
[sqlExpressionFactory.Constant(datePart), instance],
4864
nullable: true,
4965
argumentsPropagateNullability: [false, true],
50-
returnType)
51-
: null;
66+
returnType);
67+
}
5268
}

src/EFCore.Jet/Query/ExpressionTranslators/Internal/JetDateOnlyMethodTranslator.cs

Lines changed: 94 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ public class JetDateOnlyMethodTranslator(ISqlExpressionFactory sqlExpressionFact
2626
{ typeof(DateOnly).GetRuntimeMethod(nameof(DateOnly.AddDays), [typeof(int)])!, "d" }
2727
};
2828

29+
private static readonly MethodInfo ToDateTimeMethodInfo
30+
= typeof(DateOnly).GetRuntimeMethod(nameof(DateOnly.ToDateTime), [typeof(TimeOnly)])!;
31+
2932
private readonly ISqlExpressionFactory _sqlExpressionFactory = sqlExpressionFactory;
3033

3134
/// <summary>
@@ -40,27 +43,107 @@ public class JetDateOnlyMethodTranslator(ISqlExpressionFactory sqlExpressionFact
4043
IReadOnlyList<SqlExpression> arguments,
4144
IDiagnosticsLogger<DbLoggerCategory.Query> logger)
4245
{
43-
if (_methodInfoDatePartMapping.TryGetValue(method, out var datePart)
44-
&& instance != null)
46+
if (instance != null)
4547
{
46-
instance = _sqlExpressionFactory.ApplyDefaultTypeMapping(instance);
48+
if (method == ToDateTimeMethodInfo)
49+
{
50+
var timeOnly = arguments[0];
4751

48-
return _sqlExpressionFactory.Function(
49-
"DATEADD",
50-
[_sqlExpressionFactory.Constant(datePart), _sqlExpressionFactory.Convert(arguments[0], typeof(int)), instance],
51-
nullable: true,
52-
argumentsPropagateNullability: [false, false, true],
53-
instance.Type,
54-
instance.TypeMapping);
52+
// We need to refrain from doing the translation when either the DateOnly or the TimeOnly
53+
// are a complex SQL expression (anything other than a column/constant/parameter), to avoid evaluating them multiple
54+
// potentially expensive arbitrary expressions multiple times.
55+
if (instance is not ColumnExpression and not SqlParameterExpression and not SqlConstantExpression
56+
|| timeOnly is not ColumnExpression and not SqlParameterExpression and not SqlConstantExpression)
57+
{
58+
return null;
59+
}
60+
61+
return _sqlExpressionFactory.Function(
62+
"DATETIME2FROMPARTS",
63+
[
64+
MapDatePartExpression("yyyy", instance),
65+
MapDatePartExpression("m", instance),
66+
MapDatePartExpression("d", instance),
67+
MapDatePartExpression("h", timeOnly),
68+
MapDatePartExpression("n", timeOnly),
69+
MapDatePartExpression("s", timeOnly),
70+
MapDatePartExpression("fraction", timeOnly),
71+
_sqlExpressionFactory.Constant(7, typeof(int)),
72+
],
73+
nullable: true,
74+
argumentsPropagateNullability: [true, true, true, true, true, true, true, false],
75+
typeof(DateTime));
76+
}
77+
78+
if (_methodInfoDatePartMapping.TryGetValue(method, out var datePart))
79+
{
80+
instance = _sqlExpressionFactory.ApplyDefaultTypeMapping(instance);
81+
82+
return _sqlExpressionFactory.Function(
83+
"DATEADD",
84+
[_sqlExpressionFactory.Constant(datePart), _sqlExpressionFactory.Convert(arguments[0], typeof(int)), instance],
85+
nullable: true,
86+
argumentsPropagateNullability: [false, false, true],
87+
instance.Type,
88+
instance.TypeMapping);
89+
}
5590
}
91+
5692

5793
if (method.DeclaringType == typeof(DateOnly)
5894
&& method.Name == nameof(DateOnly.FromDateTime)
5995
&& arguments.Count == 1)
6096
{
61-
return _sqlExpressionFactory.Convert(arguments[0], typeof(DateOnly));
97+
return _sqlExpressionFactory.Function(
98+
"DATEVALUE",
99+
[arguments[0]],
100+
nullable: true,
101+
argumentsPropagateNullability: [false],
102+
method.ReturnType);
62103
}
63104

64105
return null;
65106
}
107+
108+
private SqlExpression MapDatePartExpression(string datepart, SqlExpression argument)
109+
{
110+
if (argument is SqlConstantExpression constantArgument)
111+
{
112+
var constant = datepart switch
113+
{
114+
"yyyy" => ((DateOnly)constantArgument.Value!).Year,
115+
"m" => ((DateOnly)constantArgument.Value!).Month,
116+
"d" => ((DateOnly)constantArgument.Value!).Day,
117+
"h" => ((TimeOnly)constantArgument.Value!).Hour,
118+
"n" => ((TimeOnly)constantArgument.Value!).Minute,
119+
"s" => ((TimeOnly)constantArgument.Value!).Second,
120+
"fraction" => ((TimeOnly)constantArgument.Value!).Ticks % 10_000_000,
121+
122+
_ => throw new UnreachableException()
123+
};
124+
125+
return _sqlExpressionFactory.Constant(constant, typeof(int));
126+
}
127+
128+
if (datepart == "fraction")
129+
{
130+
return _sqlExpressionFactory.Divide(
131+
_sqlExpressionFactory.Function(
132+
"DATEPART",
133+
[_sqlExpressionFactory.Fragment("nanosecond"), argument],
134+
nullable: true,
135+
argumentsPropagateNullability: [true, true],
136+
typeof(int)
137+
),
138+
_sqlExpressionFactory.Constant(100, typeof(int))
139+
);
140+
}
141+
142+
return _sqlExpressionFactory.Function(
143+
"DATEPART",
144+
[_sqlExpressionFactory.Constant(datepart), argument],
145+
nullable: true,
146+
argumentsPropagateNullability: [true, true],
147+
typeof(int));
148+
}
66149
}

src/EFCore.Jet/Query/Internal/JetSqlTranslatingExpressionVisitor.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
77
using System.Text;
88
using EntityFrameworkCore.Jet.Internal;
9+
using EntityFrameworkCore.Jet.Storage.Internal;
910
using ExpressionExtensions = Microsoft.EntityFrameworkCore.Query.ExpressionExtensions;
1011

1112
namespace EntityFrameworkCore.Jet.Query.Internal;
@@ -103,6 +104,27 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
103104

104105
var visitedExpression = base.VisitBinary(binaryExpression);
105106

107+
if (visitedExpression is SqlBinaryExpression be)
108+
{
109+
if (be.OperatorType is ExpressionType.And
110+
or ExpressionType.Or
111+
or ExpressionType.ExclusiveOr)
112+
{
113+
var left = CoerceIfNeeded(be.Left);
114+
var right = CoerceIfNeeded(be.Right);
115+
116+
if (!ReferenceEquals(left, be.Left) || !ReferenceEquals(right, be.Right))
117+
{
118+
visitedExpression = new SqlBinaryExpression(
119+
be.OperatorType,
120+
left,
121+
right,
122+
be.Type,
123+
be.TypeMapping);
124+
}
125+
}
126+
}
127+
106128
if (visitedExpression is SqlBinaryExpression sqlBinaryExpression
107129
&& ArithmeticOperatorTypes.Contains(sqlBinaryExpression.OperatorType))
108130
{
@@ -129,6 +151,25 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
129151
return visitedExpression;
130152
}
131153

154+
private SqlExpression CoerceIfNeeded(SqlExpression operand)
155+
{
156+
// Skip if already a LONG (Jet) or if already a CLNG(...) call
157+
var storeType = operand.TypeMapping?.StoreType;
158+
if (storeType is "long" or "integer" or "int") return operand;
159+
var clr = operand.Type;
160+
if (clr == typeof(byte) || clr == typeof(short))
161+
{
162+
return _sqlExpressionFactory.Function(
163+
"CLNG",
164+
[operand],
165+
true,
166+
argumentsPropagateNullability: [true],
167+
typeof(long),
168+
IntTypeMapping.Default);
169+
}
170+
return operand;
171+
}
172+
132173
/// <summary>
133174
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
134175
/// the same compatibility standards as public APIs. It may be changed or removed without notice in

src/EFCore.Jet/Query/Sql/Internal/JetQuerySqlGenerator.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,13 @@ private Expression VisitJetConvertExpression(SqlUnaryExpression convertExpressio
629629
{
630630
notnullsqlexp = new SqlFunctionExpression(function, [convertExpression.Operand],
631631
false, [false], typeMapping.ClrType, null);
632+
if (convertExpression.Operand.Type == typeof(bool))
633+
{
634+
//create a new expression to multiply the result by -1 to flip the boolean value
635+
notnullsqlexp = new SqlBinaryExpression(ExpressionType.Multiply, notnullsqlexp,
636+
new SqlConstantExpression(-1, IntTypeMapping.Default), notnullsqlexp.Type, notnullsqlexp.TypeMapping);
637+
}
638+
632639
}
633640

634641
SqlConstantExpression nullcons = new(null,typeof(string), RelationalTypeMapping.NullMapping);

test/Directory.Build.props

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
<PropertyGroup>
55
<NoWarn>$(NoWarn);CA1707;1591;xUnit1000;xUnit1003;xUnit1004;xUnit1010;xUnit1013;xUnit1026;xUnit2013;xUnit1024;NU1903;EF1001</NoWarn>
6-
<LangVersion>13.0</LangVersion>
76
<DefaultNetCoreTargetFramework>net9.0</DefaultNetCoreTargetFramework>
87
<!-- TODO: Change to "'$(FixedTestOrder)' != 'true'" once test suite is stable. -->
98
<DefineConstants Condition="'$(FixedTestOrder)' != 'false'">$(DefineConstants);FIXED_TEST_ORDER</DefineConstants>

0 commit comments

Comments
 (0)