Skip to content

Commit f2e0161

Browse files
committed
Fix to #30996 - Incorrect translation of comparison of current value with owned type default value
In optional dependent table sharing scenario, when comparing the dependent to null we first look for any required properties (at least one of them needs to be null), and if we don't have any required properties we look at optional ones (even though this is somewhat ambiguous). Error was that we did similar check as with required properties (i.e. at least one of them must be null), but what we should be doing is checking that all of them are null. Fixes #30996
1 parent cc6c986 commit f2e0161

File tree

3 files changed

+96
-2
lines changed

3 files changed

+96
-2
lines changed

src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1797,7 +1797,7 @@ bool TryRewriteEntityEquality([NotNullWhen(true)] out Expression? result)
17971797
CreatePropertyAccessExpression(nonNullEntityReference, p),
17981798
Expression.Constant(null, p.ClrType.MakeNullable()),
17991799
nodeType != ExpressionType.Equal))
1800-
.Aggregate((l, r) => nodeType == ExpressionType.Equal ? Expression.OrElse(l, r) : Expression.AndAlso(l, r));
1800+
.Aggregate((l, r) => nodeType == ExpressionType.Equal ? Expression.AndAlso(l, r) : Expression.OrElse(l, r));
18011801

18021802
condition = condition == null
18031803
? atLeastOneNonNullValueInNullablePropertyCondition

test/EFCore.Relational.Specification.Tests/Query/OwnedEntityQueryRelationalTestBase.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,64 @@ public virtual async Task Owned_entity_with_all_null_properties_entity_equality_
284284
});
285285
}
286286

287+
[ConditionalTheory]
288+
[MemberData(nameof(IsAsyncData))]
289+
public virtual async Task Owned_entity_with_all_null_properties_in_compared_to_null_in_conditional_projection(bool async)
290+
{
291+
var contextFactory = await InitializeAsync<MyContext28247>(seed: c => c.Seed());
292+
293+
using var context = contextFactory.CreateContext();
294+
var query = context.RotRutCases
295+
.AsNoTracking()
296+
.OrderBy(e => e.Id)
297+
.Select(e => e.Rot == null ? null : new RotDto { MyApartmentNo = e.Rot.ApartmentNo, MyServiceType = e.Rot.ServiceType });
298+
299+
var result = async
300+
? await query.ToListAsync()
301+
: query.ToList();
302+
303+
Assert.Collection(
304+
result,
305+
t =>
306+
{
307+
Assert.Equal("1", t.MyApartmentNo);
308+
Assert.Equal(1, t.MyServiceType);
309+
},
310+
t =>
311+
{
312+
Assert.Null(t);
313+
});
314+
}
315+
316+
[ConditionalTheory]
317+
[MemberData(nameof(IsAsyncData))]
318+
public virtual async Task Owned_entity_with_all_null_properties_in_compared_to_non_null_in_conditional_projection(bool async)
319+
{
320+
var contextFactory = await InitializeAsync<MyContext28247>(seed: c => c.Seed());
321+
322+
using var context = contextFactory.CreateContext();
323+
var query = context.RotRutCases
324+
.AsNoTracking()
325+
.OrderBy(e => e.Id)
326+
.Select(e => e.Rot != null ? new RotDto { MyApartmentNo = e.Rot.ApartmentNo, MyServiceType = e.Rot.ServiceType } : null);
327+
328+
var result = async
329+
? await query.ToListAsync()
330+
: query.ToList();
331+
332+
Assert.Collection(
333+
result,
334+
t =>
335+
{
336+
Assert.Equal("1", t.MyApartmentNo);
337+
Assert.Equal(1, t.MyServiceType);
338+
},
339+
t =>
340+
{
341+
Assert.Null(t);
342+
});
343+
}
344+
287345
[ConditionalTheory]
288346
[MemberData(nameof(IsAsyncData))]
289347
public virtual async Task Owned_entity_with_all_null_properties_property_access_when_not_containing_another_owned_entity(bool async)
@@ -364,6 +422,12 @@ public class Rot
364422
public string ApartmentNo { get; set; }
365423
}
366424

425+
public class RotDto
426+
{
427+
public int? MyServiceType { get; set; }
428+
public string MyApartmentNo { get; set; }
429+
}
430+
367431
public class Rut
368432
{
369433
public int? Value { get; set; }

test/EFCore.SqlServer.FunctionalTests/Query/OwnedEntityQuerySqlServerTest.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,37 @@ public override async Task Owned_entity_with_all_null_properties_entity_equality
166166
"""
167167
SELECT [r].[Id], [r].[Rot_ApartmentNo], [r].[Rot_ServiceType]
168168
FROM [RotRutCases] AS [r]
169-
WHERE [r].[Rot_ApartmentNo] IS NOT NULL AND [r].[Rot_ServiceType] IS NOT NULL
169+
WHERE [r].[Rot_ApartmentNo] IS NOT NULL OR [r].[Rot_ServiceType] IS NOT NULL
170+
""");
171+
}
172+
173+
public override async Task Owned_entity_with_all_null_properties_in_compared_to_null_in_conditional_projection(bool async)
174+
{
175+
await base.Owned_entity_with_all_null_properties_in_compared_to_null_in_conditional_projection(async);
176+
177+
AssertSql(
178+
"""
179+
SELECT CASE
180+
WHEN [r].[Rot_ApartmentNo] IS NULL AND [r].[Rot_ServiceType] IS NULL THEN CAST(1 AS bit)
181+
ELSE CAST(0 AS bit)
182+
END, [r].[Rot_ApartmentNo], [r].[Rot_ServiceType]
183+
FROM [RotRutCases] AS [r]
184+
ORDER BY [r].[Id]
185+
""");
186+
}
187+
188+
public override async Task Owned_entity_with_all_null_properties_in_compared_to_non_null_in_conditional_projection(bool async)
189+
{
190+
await base.Owned_entity_with_all_null_properties_in_compared_to_non_null_in_conditional_projection(async);
191+
192+
AssertSql(
193+
"""
194+
SELECT CASE
195+
WHEN [r].[Rot_ApartmentNo] IS NOT NULL OR [r].[Rot_ServiceType] IS NOT NULL THEN CAST(1 AS bit)
196+
ELSE CAST(0 AS bit)
197+
END, [r].[Rot_ApartmentNo], [r].[Rot_ServiceType]
198+
FROM [RotRutCases] AS [r]
199+
ORDER BY [r].[Id]
170200
""");
171201
}
172202

0 commit comments

Comments
 (0)