Skip to content

Commit 27a83b9

Browse files
authored
Query: Improve logic to find column names in TPC (#28198)
Resolves #28196
1 parent 0c3b427 commit 27a83b9

File tree

4 files changed

+155
-8
lines changed

4 files changed

+155
-8
lines changed

src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -224,18 +224,17 @@ internal SelectExpression(IEntityType entityType, ISqlExpressionFactory sqlExpre
224224
var tables = entityTypes.Select(e => GetTableBase(e)).ToArray();
225225
var properties = GetAllPropertiesInHierarchy(entityType).ToArray();
226226
var propertyNamesMap = new Dictionary<IProperty, string>();
227-
foreach (var property in entityTypes[0].GetProperties())
228-
{
229-
propertyNamesMap[property] = tables[0].FindColumn(property)!.Name;
230-
}
231-
for (var i = 1; i < entityTypes.Length; i++)
227+
for (var i = 0; i < entityTypes.Length; i++)
232228
{
233-
foreach (var property in entityTypes[i].GetDeclaredProperties())
229+
foreach (var property in entityTypes[i].GetProperties())
234230
{
235-
Check.DebugAssert(!propertyNamesMap.ContainsKey(property), "Duplicate property found.");
236-
propertyNamesMap[property] = tables[i].FindColumn(property)!.Name;
231+
if (!propertyNamesMap.ContainsKey(property))
232+
{
233+
propertyNamesMap[property] = tables[i].FindColumn(property)!.Name;
234+
}
237235
}
238236
}
237+
239238
var propertyNames = new string[properties.Length];
240239
for (var i = 0; i < properties.Length; i++)
241240
{

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,36 @@ protected class MyEntity
101101
public DateTime SomeDate { get; set; }
102102
public static DateTime Modify(DateTime date) => throw new NotSupportedException();
103103
}
104+
105+
[ConditionalTheory]
106+
[MemberData(nameof(IsAsyncData))]
107+
public virtual Task Hierarchy_query_with_abstract_type_sibling_TPC(bool async)
108+
{
109+
return Hierarchy_query_with_abstract_type_sibling_helper(async,
110+
mb =>
111+
{
112+
mb.Entity<Animal>().UseTpcMappingStrategy();
113+
mb.Entity<Pet>().ToTable("Pets");
114+
mb.Entity<Cat>().ToTable("Cats");
115+
mb.Entity<Dog>().ToTable("Dogs");
116+
mb.Entity<FarmAnimal>().ToTable("FarmAnimals");
117+
});
118+
}
119+
120+
[ConditionalTheory]
121+
[MemberData(nameof(IsAsyncData))]
122+
public virtual Task Hierarchy_query_with_abstract_type_sibling_TPT(bool async)
123+
{
124+
return Hierarchy_query_with_abstract_type_sibling_helper(async,
125+
mb =>
126+
{
127+
mb.Entity<Animal>().UseTptMappingStrategy();
128+
mb.Entity<Pet>().ToTable("Pets");
129+
mb.Entity<Cat>().ToTable("Cats");
130+
mb.Entity<Dog>().ToTable("Dogs");
131+
mb.Entity<FarmAnimal>().ToTable("FarmAnimals");
132+
});
133+
}
104134
}
105135
}
106136

test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.cs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,4 +1256,80 @@ protected class SearchResult
12561256
public int RowId { get; set; }
12571257
public string DistinctValue { get; set; }
12581258
}
1259+
1260+
[ConditionalTheory]
1261+
[MemberData(nameof(IsAsyncData))]
1262+
public virtual Task Hierarchy_query_with_abstract_type_sibling(bool async)
1263+
{
1264+
return Hierarchy_query_with_abstract_type_sibling_helper(async, null);
1265+
}
1266+
1267+
public virtual async Task Hierarchy_query_with_abstract_type_sibling_helper(bool async, Action<ModelBuilder> onModelCreating)
1268+
{
1269+
var contextFactory = await InitializeAsync<Context28196>(onModelCreating: onModelCreating, seed: c => c.Seed());
1270+
using var context = contextFactory.CreateContext();
1271+
1272+
var query = context.Animals.OfType<Pet>().Where(a => a.Species.StartsWith("F"));
1273+
1274+
var result = async
1275+
? await query.ToListAsync()
1276+
: query.ToList();
1277+
}
1278+
1279+
protected class Context28196 : DbContext
1280+
{
1281+
public Context28196(DbContextOptions options)
1282+
: base(options)
1283+
{
1284+
}
1285+
1286+
public DbSet<Animal> Animals { get; set; }
1287+
1288+
protected override void OnModelCreating(ModelBuilder modelBuilder)
1289+
{
1290+
modelBuilder.Entity<Animal>().Property(e => e.Id).ValueGeneratedNever();
1291+
modelBuilder.Entity<Pet>();
1292+
modelBuilder.Entity<Cat>();
1293+
modelBuilder.Entity<Dog>();
1294+
modelBuilder.Entity<FarmAnimal>();
1295+
}
1296+
1297+
public void Seed()
1298+
{
1299+
AddRange(
1300+
new Cat { Id = 1, Name = "Alice", Species = "Felis catus", EdcuationLevel = "MBA" },
1301+
new Cat { Id = 2, Name = "Mac", Species = "Felis catus", EdcuationLevel = "BA" },
1302+
new Dog { Id = 3, Name = "Toast", Species = "Canis familiaris", FavoriteToy = "Mr. Squirrel" },
1303+
new FarmAnimal { Id = 4, Value = 100.0, Species = "Ovis aries" });
1304+
1305+
SaveChanges();
1306+
}
1307+
}
1308+
1309+
protected abstract class Animal
1310+
{
1311+
public int Id { get; set; }
1312+
public string Species { get; set; }
1313+
}
1314+
1315+
protected class FarmAnimal : Animal
1316+
{
1317+
public double Value { get; set; }
1318+
}
1319+
1320+
protected abstract class Pet : Animal
1321+
{
1322+
public string Name { get; set; }
1323+
}
1324+
1325+
protected class Cat : Pet
1326+
{
1327+
public string EdcuationLevel { get; set; }
1328+
}
1329+
1330+
protected class Dog : Pet
1331+
{
1332+
public string FavoriteToy { get; set; }
1333+
}
1334+
12591335
}

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,4 +429,46 @@ HAVING COUNT(*) = 1
429429
WHERE [t].[TableId] = 123
430430
ORDER BY [t].[ParcelNumber]");
431431
}
432+
433+
public override async Task Hierarchy_query_with_abstract_type_sibling(bool async)
434+
{
435+
await base.Hierarchy_query_with_abstract_type_sibling(async);
436+
437+
AssertSql(
438+
@"SELECT [a].[Id], [a].[Discriminator], [a].[Species], [a].[Name], [a].[EdcuationLevel], [a].[FavoriteToy]
439+
FROM [Animals] AS [a]
440+
WHERE [a].[Discriminator] IN (N'Cat', N'Dog') AND [a].[Species] IS NOT NULL AND ([a].[Species] LIKE N'F%')");
441+
}
442+
443+
public override async Task Hierarchy_query_with_abstract_type_sibling_TPT(bool async)
444+
{
445+
await base.Hierarchy_query_with_abstract_type_sibling_TPT(async);
446+
447+
AssertSql(
448+
@"SELECT [a].[Id], [a].[Species], [p].[Name], [c].[EdcuationLevel], [d].[FavoriteToy], CASE
449+
WHEN [d].[Id] IS NOT NULL THEN N'Dog'
450+
WHEN [c].[Id] IS NOT NULL THEN N'Cat'
451+
END AS [Discriminator]
452+
FROM [Animals] AS [a]
453+
LEFT JOIN [Pets] AS [p] ON [a].[Id] = [p].[Id]
454+
LEFT JOIN [Cats] AS [c] ON [a].[Id] = [c].[Id]
455+
LEFT JOIN [Dogs] AS [d] ON [a].[Id] = [d].[Id]
456+
WHERE ([d].[Id] IS NOT NULL OR [c].[Id] IS NOT NULL) AND [a].[Species] IS NOT NULL AND ([a].[Species] LIKE N'F%')");
457+
}
458+
459+
public override async Task Hierarchy_query_with_abstract_type_sibling_TPC(bool async)
460+
{
461+
await base.Hierarchy_query_with_abstract_type_sibling_TPC(async);
462+
463+
AssertSql(
464+
@"SELECT [t].[Id], [t].[Species], [t].[Name], [t].[EdcuationLevel], [t].[FavoriteToy], [t].[Discriminator]
465+
FROM (
466+
SELECT [c].[Id], [c].[Species], [c].[Name], [c].[EdcuationLevel], NULL AS [FavoriteToy], N'Cat' AS [Discriminator]
467+
FROM [Cats] AS [c]
468+
UNION ALL
469+
SELECT [d].[Id], [d].[Species], [d].[Name], NULL AS [EdcuationLevel], [d].[FavoriteToy], N'Dog' AS [Discriminator]
470+
FROM [Dogs] AS [d]
471+
) AS [t]
472+
WHERE [t].[Species] IS NOT NULL AND ([t].[Species] LIKE N'F%')");
473+
}
432474
}

0 commit comments

Comments
 (0)