Skip to content

Commit e119e1f

Browse files
authored
Fix to #35299 - EF Core InMemory database throws an argument exception on Nullable<>.ToString (#35763)
Problem was that when rewriting the (inmemory) query, sometimes we change the nullability of the expression fragment. (e.g. when accessing a property on an optional entity). We then have a logic to compensate for the change. E.g. when the modified fragment was a caller of a method, we add a null check and only call the method (as non-nullable argument) if the value is not null. This way we can retain the method signature as it was. In 9, we added some exemptions to this logic, e.g. `GetValueOrDefault`, or `Nullable<>.ToString`, which don't need the compensation. Problem was that we were matching the ToString method incorrectly, only looking at the method name, so we would also match non-nullable ToString(). Fix is to look at the declaring type of the ToString method as well to make sure it's nullable<T>, otherwise apply the compensation. Fixes #35299
1 parent 666dc21 commit e119e1f

File tree

2 files changed

+28
-2
lines changed

2 files changed

+28
-2
lines changed

src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -888,8 +888,10 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
888888
if (methodCallExpression.Object != null
889889
&& @object!.Type.IsNullableType()
890890
&& methodCallExpression.Method.Name != nameof(Nullable<int>.GetValueOrDefault)
891-
&& (!@object!.Type.IsNullableValueType()
892-
|| methodCallExpression.Method.Name != nameof(Nullable<int>.ToString)))
891+
&& !(@object!.Type.IsNullableValueType()
892+
&& methodCallExpression.Method.Name == nameof(Nullable<int>.ToString)
893+
&& methodCallExpression.Method.DeclaringType != null
894+
&& methodCallExpression.Method.DeclaringType.IsNullableType()))
893895
{
894896
var result = (Expression)methodCallExpression.Update(
895897
Expression.Convert(@object, methodCallExpression.Object.Type),

test/EFCore.InMemory.FunctionalTests/Query/GearsOfWarQueryInMemoryTest.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,28 @@ public override Task Join_include_conditional(bool async)
147147
// Right join not supported in InMemory
148148
public override Task Correlated_collections_on_RightJoin_with_predicate(bool async)
149149
=> AssertTranslationFailed(() => base.Correlated_collections_on_RightJoin_with_predicate(async));
150+
151+
[ConditionalTheory]
152+
[MemberData(nameof(IsAsyncData))]
153+
public virtual Task Select_ToString_on_non_nullable_property_of_an_optional_entity(bool async)
154+
{
155+
return AssertQuery(
156+
async,
157+
ss => ss.Set<CogTag>().Select(x => new
158+
{
159+
Id = x.Id,
160+
SquadIdString = x.Gear.SquadId.ToString()
161+
}),
162+
ss => ss.Set<CogTag>().Select(x => new
163+
{
164+
Id = x.Id,
165+
SquadIdString = x.Gear == null ? null : x.Gear.SquadId.ToString()
166+
}),
167+
elementSorter: e => e.Id,
168+
elementAsserter: (e, a) =>
169+
{
170+
AssertEqual(e.Id, a.Id);
171+
AssertEqual(e.SquadIdString, a.SquadIdString);
172+
});
173+
}
150174
}

0 commit comments

Comments
 (0)