Skip to content

Commit c10608f

Browse files
committed
Refactor method/member translators to stop using reflection lookup
See dotnet/efcore#27113
1 parent 5604097 commit c10608f

21 files changed

Lines changed: 871 additions & 1643 deletions

src/EFCore.PG.NodaTime/Query/Internal/NpgsqlNodaTimeEvaluatableExpressionFilterPlugin.cs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,6 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime.Query.Internal;
88
/// </summary>
99
public class NpgsqlNodaTimeEvaluatableExpressionFilterPlugin : IEvaluatableExpressionFilterPlugin
1010
{
11-
private static readonly MethodInfo GetCurrentInstantMethod =
12-
typeof(SystemClock).GetRuntimeMethod(nameof(SystemClock.GetCurrentInstant), [])!;
13-
14-
private static readonly MemberInfo SystemClockInstanceMember =
15-
typeof(SystemClock).GetMember(nameof(SystemClock.Instance)).FirstOrDefault()!;
16-
1711
/// <summary>
1812
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
1913
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -24,11 +18,14 @@ public virtual bool IsEvaluatableExpression(Expression expression)
2418
{
2519
switch (expression)
2620
{
27-
case MethodCallExpression methodCallExpression when methodCallExpression.Method == GetCurrentInstantMethod:
21+
case MethodCallExpression methodCallExpression
22+
when methodCallExpression.Method.DeclaringType == typeof(SystemClock)
23+
&& methodCallExpression.Method.Name == nameof(SystemClock.GetCurrentInstant):
2824
return false;
2925

3026
case MemberExpression memberExpression:
31-
if (memberExpression.Member == SystemClockInstanceMember)
27+
if (memberExpression.Member.DeclaringType == typeof(SystemClock)
28+
&& memberExpression.Member.Name == nameof(SystemClock.Instance))
3229
{
3330
return false;
3431
}

src/EFCore.PG.NodaTime/Query/Internal/NpgsqlNodaTimeMemberTranslatorPlugin.cs

Lines changed: 62 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,22 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime.Query.Internal;
99
/// <remarks>
1010
/// See: https://www.postgresql.org/docs/current/static/functions-datetime.html
1111
/// </remarks>
12-
public class NpgsqlNodaTimeMemberTranslatorPlugin : IMemberTranslatorPlugin
12+
public class NpgsqlNodaTimeMemberTranslatorPlugin(
13+
IRelationalTypeMappingSource typeMappingSource,
14+
ISqlExpressionFactory sqlExpressionFactory)
15+
: IMemberTranslatorPlugin
1316
{
17+
1418
/// <summary>
1519
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
1620
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
1721
/// any release. You should only use it directly in your code with extreme caution and knowing that
1822
/// doing so can result in application failures when updating to a new Entity Framework Core release.
1923
/// </summary>
20-
public NpgsqlNodaTimeMemberTranslatorPlugin(
21-
IRelationalTypeMappingSource typeMappingSource,
22-
ISqlExpressionFactory sqlExpressionFactory)
23-
{
24-
Translators =
24+
public virtual IEnumerable<IMemberTranslator> Translators { get; } =
2525
[
2626
new NpgsqlNodaTimeMemberTranslator(typeMappingSource, (NpgsqlSqlExpressionFactory)sqlExpressionFactory)
2727
];
28-
}
29-
30-
/// <summary>
31-
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
32-
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
33-
/// any release. You should only use it directly in your code with extreme caution and knowing that
34-
/// doing so can result in application failures when updating to a new Entity Framework Core release.
35-
/// </summary>
36-
public virtual IEnumerable<IMemberTranslator> Translators { get; }
3728
}
3829

3930
/// <summary>
@@ -42,64 +33,22 @@ public NpgsqlNodaTimeMemberTranslatorPlugin(
4233
/// any release. You should only use it directly in your code with extreme caution and knowing that
4334
/// doing so can result in application failures when updating to a new Entity Framework Core release.
4435
/// </summary>
45-
public class NpgsqlNodaTimeMemberTranslator : IMemberTranslator
36+
/// <remarks>
37+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
38+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
39+
/// any release. You should only use it directly in your code with extreme caution and knowing that
40+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
41+
/// </remarks>
42+
public class NpgsqlNodaTimeMemberTranslator(
43+
IRelationalTypeMappingSource typeMappingSource,
44+
NpgsqlSqlExpressionFactory sqlExpressionFactory)
45+
: IMemberTranslator
4646
{
47-
private static readonly MemberInfo SystemClock_Instance =
48-
typeof(SystemClock).GetRuntimeProperty(nameof(SystemClock.Instance))!;
49-
50-
private static readonly MemberInfo ZonedDateTime_LocalDateTime =
51-
typeof(ZonedDateTime).GetRuntimeProperty(nameof(ZonedDateTime.LocalDateTime))!;
52-
53-
private static readonly MemberInfo Interval_Start =
54-
typeof(Interval).GetRuntimeProperty(nameof(Interval.Start))!;
55-
56-
private static readonly MemberInfo Interval_End =
57-
typeof(Interval).GetRuntimeProperty(nameof(Interval.End))!;
58-
59-
private static readonly MemberInfo Interval_HasStart =
60-
typeof(Interval).GetRuntimeProperty(nameof(Interval.HasStart))!;
61-
62-
private static readonly MemberInfo Interval_HasEnd =
63-
typeof(Interval).GetRuntimeProperty(nameof(Interval.HasEnd))!;
64-
65-
private static readonly MemberInfo Interval_Duration =
66-
typeof(Interval).GetRuntimeProperty(nameof(Interval.Duration))!;
67-
68-
private static readonly MemberInfo DateInterval_Start =
69-
typeof(DateInterval).GetRuntimeProperty(nameof(DateInterval.Start))!;
70-
71-
private static readonly MemberInfo DateInterval_End =
72-
typeof(DateInterval).GetRuntimeProperty(nameof(DateInterval.End))!;
73-
74-
private static readonly MemberInfo DateInterval_Length =
75-
typeof(DateInterval).GetRuntimeProperty(nameof(DateInterval.Length))!;
76-
77-
private static readonly MemberInfo DateTimeZoneProviders_TzDb =
78-
typeof(DateTimeZoneProviders).GetRuntimeProperty(nameof(DateTimeZoneProviders.Tzdb))!;
79-
80-
private readonly NpgsqlSqlExpressionFactory _sqlExpressionFactory;
81-
private readonly IRelationalTypeMappingSource _typeMappingSource;
82-
private readonly RelationalTypeMapping _dateTypeMapping;
83-
private readonly RelationalTypeMapping _periodTypeMapping;
84-
private readonly RelationalTypeMapping _localDateTimeTypeMapping;
85-
86-
/// <summary>
87-
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
88-
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
89-
/// any release. You should only use it directly in your code with extreme caution and knowing that
90-
/// doing so can result in application failures when updating to a new Entity Framework Core release.
91-
/// </summary>
92-
public NpgsqlNodaTimeMemberTranslator(
93-
IRelationalTypeMappingSource typeMappingSource,
94-
NpgsqlSqlExpressionFactory sqlExpressionFactory)
95-
{
96-
_typeMappingSource = typeMappingSource;
97-
_sqlExpressionFactory = sqlExpressionFactory;
98-
_dateTypeMapping = typeMappingSource.FindMapping(typeof(LocalDate))!;
99-
_periodTypeMapping = typeMappingSource.FindMapping(typeof(Period))!;
100-
_localDateTimeTypeMapping = typeMappingSource.FindMapping(typeof(LocalDateTime))!;
101-
}
102-
47+
private readonly NpgsqlSqlExpressionFactory _sqlExpressionFactory = sqlExpressionFactory;
48+
private readonly IRelationalTypeMappingSource _typeMappingSource = typeMappingSource;
49+
private readonly RelationalTypeMapping _dateTypeMapping = typeMappingSource.FindMapping(typeof(LocalDate))!;
50+
private readonly RelationalTypeMapping _periodTypeMapping = typeMappingSource.FindMapping(typeof(Period))!;
51+
private readonly RelationalTypeMapping _localDateTimeTypeMapping = typeMappingSource.FindMapping(typeof(LocalDateTime))!;
10352
private static readonly bool[][] TrueArrays = [[], [true], [true, true]];
10453

10554
/// <inheritdoc />
@@ -110,12 +59,12 @@ public NpgsqlNodaTimeMemberTranslator(
11059
IDiagnosticsLogger<DbLoggerCategory.Query> logger)
11160
{
11261
// This is necessary to allow translation of methods on SystemClock.Instance
113-
if (member == SystemClock_Instance)
62+
if (member.DeclaringType == typeof(SystemClock) && member.Name == nameof(SystemClock.Instance))
11463
{
11564
return _sqlExpressionFactory.Constant(SystemClock.Instance);
11665
}
11766

118-
if (member == DateTimeZoneProviders_TzDb)
67+
if (member.DeclaringType == typeof(DateTimeZoneProviders) && member.Name == nameof(DateTimeZoneProviders.Tzdb))
11968
{
12069
return PendingDateTimeZoneProviderExpression.Instance;
12170
}
@@ -181,44 +130,34 @@ SqlExpression TranslateDurationTotalMember(SqlExpression instance, double diviso
181130

182131
private SqlExpression? TranslateInterval(SqlExpression instance, MemberInfo member)
183132
{
184-
if (member == Interval_Start)
185-
{
186-
return Lower();
187-
}
188-
189-
if (member == Interval_End)
190-
{
191-
return Upper();
192-
}
193-
194-
if (member == Interval_HasStart)
195-
{
196-
return _sqlExpressionFactory.Not(
197-
_sqlExpressionFactory.Function(
198-
"lower_inf",
199-
[instance],
200-
nullable: true,
201-
argumentsPropagateNullability: TrueArrays[1],
202-
typeof(bool)));
203-
}
204-
205-
if (member == Interval_HasEnd)
206-
{
207-
return _sqlExpressionFactory.Not(
208-
_sqlExpressionFactory.Function(
209-
"upper_inf",
210-
[instance],
211-
nullable: true,
212-
argumentsPropagateNullability: TrueArrays[1],
213-
typeof(bool)));
214-
}
215-
216-
if (member == Interval_Duration)
133+
return member.Name switch
217134
{
218-
return _sqlExpressionFactory.Subtract(Upper(), Lower(), _typeMappingSource.FindMapping(typeof(Duration)));
219-
}
135+
nameof(Interval.Start) => Lower(),
136+
nameof(Interval.End) => Upper(),
137+
138+
nameof(Interval.HasStart)
139+
=> _sqlExpressionFactory.Not(
140+
_sqlExpressionFactory.Function(
141+
"lower_inf",
142+
[instance],
143+
nullable: true,
144+
argumentsPropagateNullability: TrueArrays[1],
145+
typeof(bool))),
146+
147+
nameof(Interval.HasEnd)
148+
=> _sqlExpressionFactory.Not(
149+
_sqlExpressionFactory.Function(
150+
"upper_inf",
151+
[instance],
152+
nullable: true,
153+
argumentsPropagateNullability: TrueArrays[1],
154+
typeof(bool))),
155+
156+
nameof(Interval.Duration)
157+
=> _sqlExpressionFactory.Subtract(Upper(), Lower(), _typeMappingSource.FindMapping(typeof(Duration))),
220158

221-
return null;
159+
_ => null
160+
};
222161

223162
SqlExpression Lower()
224163
=> _sqlExpressionFactory.Function(
@@ -244,28 +183,23 @@ SqlExpression Upper()
244183
// NodaTime DateInterval is inclusive on both ends.
245184
// PostgreSQL daterange is a discrete range type; this means it gets normalized to inclusive lower bound, exclusive upper bound.
246185
// So we can translate Start as-is, but need to subtract a day for End.
247-
if (member == DateInterval_Start)
186+
return member.Name switch
248187
{
249-
return Lower();
250-
}
188+
nameof(DateInterval.Start) => Lower(),
251189

252-
if (member == DateInterval_End)
253-
{
254190
// PostgreSQL creates a result of type 'timestamp without time zone' when subtracting intervals from dates, so add a cast back
255191
// to date.
256-
return _sqlExpressionFactory.Convert(
257-
_sqlExpressionFactory.Subtract(
258-
Upper(),
259-
_sqlExpressionFactory.Constant(Period.FromDays(1), _periodTypeMapping)), typeof(LocalDate),
260-
_typeMappingSource.FindMapping(typeof(LocalDate)));
261-
}
192+
nameof(DateInterval.End)
193+
=> _sqlExpressionFactory.Convert(
194+
_sqlExpressionFactory.Subtract(
195+
Upper(),
196+
_sqlExpressionFactory.Constant(Period.FromDays(1), _periodTypeMapping)), typeof(LocalDate),
197+
_typeMappingSource.FindMapping(typeof(LocalDate))),
262198

263-
if (member == DateInterval_Length)
264-
{
265-
return _sqlExpressionFactory.Subtract(Upper(), Lower());
266-
}
199+
nameof(DateInterval.Length) => _sqlExpressionFactory.Subtract(Upper(), Lower()),
267200

268-
return null;
201+
_ => null
202+
};
269203

270204
SqlExpression Lower()
271205
=> _sqlExpressionFactory.Function(
@@ -378,7 +312,7 @@ private SqlExpression GetDatePartExpressionDouble(
378312
typeof(LocalDateTime),
379313
_localDateTimeTypeMapping);
380314

381-
return member == ZonedDateTime_LocalDateTime
315+
return member.Name == nameof(ZonedDateTime.LocalDateTime)
382316
? instance
383317
: TranslateDateTime(instance, member);
384318
}
@@ -388,7 +322,7 @@ private SqlExpression GetDatePartExpressionDouble(
388322
// The same works also for the LocalDateTime member.
389323
instance = _sqlExpressionFactory.AtUtc(instance);
390324

391-
return member == ZonedDateTime_LocalDateTime
325+
return member.Name == nameof(ZonedDateTime.LocalDateTime)
392326
? instance
393327
: TranslateDateTime(instance, member);
394328
}

0 commit comments

Comments
 (0)