Skip to content

Commit

Permalink
Query: Improve logic to find column names in TPC (dotnet#28198)
Browse files Browse the repository at this point in the history
Resolves dotnet#28196
  • Loading branch information
smitpatel authored Jun 9, 2022
1 parent 0c3b427 commit 27a83b9
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 8 deletions.
15 changes: 7 additions & 8 deletions src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,18 +224,17 @@ internal SelectExpression(IEntityType entityType, ISqlExpressionFactory sqlExpre
var tables = entityTypes.Select(e => GetTableBase(e)).ToArray();
var properties = GetAllPropertiesInHierarchy(entityType).ToArray();
var propertyNamesMap = new Dictionary<IProperty, string>();
foreach (var property in entityTypes[0].GetProperties())
{
propertyNamesMap[property] = tables[0].FindColumn(property)!.Name;
}
for (var i = 1; i < entityTypes.Length; i++)
for (var i = 0; i < entityTypes.Length; i++)
{
foreach (var property in entityTypes[i].GetDeclaredProperties())
foreach (var property in entityTypes[i].GetProperties())
{
Check.DebugAssert(!propertyNamesMap.ContainsKey(property), "Duplicate property found.");
propertyNamesMap[property] = tables[i].FindColumn(property)!.Name;
if (!propertyNamesMap.ContainsKey(property))
{
propertyNamesMap[property] = tables[i].FindColumn(property)!.Name;
}
}
}

var propertyNames = new string[properties.Length];
for (var i = 0; i < properties.Length; i++)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,36 @@ protected class MyEntity
public DateTime SomeDate { get; set; }
public static DateTime Modify(DateTime date) => throw new NotSupportedException();
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Hierarchy_query_with_abstract_type_sibling_TPC(bool async)
{
return Hierarchy_query_with_abstract_type_sibling_helper(async,
mb =>
{
mb.Entity<Animal>().UseTpcMappingStrategy();
mb.Entity<Pet>().ToTable("Pets");
mb.Entity<Cat>().ToTable("Cats");
mb.Entity<Dog>().ToTable("Dogs");
mb.Entity<FarmAnimal>().ToTable("FarmAnimals");
});
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Hierarchy_query_with_abstract_type_sibling_TPT(bool async)
{
return Hierarchy_query_with_abstract_type_sibling_helper(async,
mb =>
{
mb.Entity<Animal>().UseTptMappingStrategy();
mb.Entity<Pet>().ToTable("Pets");
mb.Entity<Cat>().ToTable("Cats");
mb.Entity<Dog>().ToTable("Dogs");
mb.Entity<FarmAnimal>().ToTable("FarmAnimals");
});
}
}
}

Expand Down
76 changes: 76 additions & 0 deletions test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1256,4 +1256,80 @@ protected class SearchResult
public int RowId { get; set; }
public string DistinctValue { get; set; }
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Hierarchy_query_with_abstract_type_sibling(bool async)
{
return Hierarchy_query_with_abstract_type_sibling_helper(async, null);
}

public virtual async Task Hierarchy_query_with_abstract_type_sibling_helper(bool async, Action<ModelBuilder> onModelCreating)
{
var contextFactory = await InitializeAsync<Context28196>(onModelCreating: onModelCreating, seed: c => c.Seed());
using var context = contextFactory.CreateContext();

var query = context.Animals.OfType<Pet>().Where(a => a.Species.StartsWith("F"));

var result = async
? await query.ToListAsync()
: query.ToList();
}

protected class Context28196 : DbContext
{
public Context28196(DbContextOptions options)
: base(options)
{
}

public DbSet<Animal> Animals { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Animal>().Property(e => e.Id).ValueGeneratedNever();
modelBuilder.Entity<Pet>();
modelBuilder.Entity<Cat>();
modelBuilder.Entity<Dog>();
modelBuilder.Entity<FarmAnimal>();
}

public void Seed()
{
AddRange(
new Cat { Id = 1, Name = "Alice", Species = "Felis catus", EdcuationLevel = "MBA" },
new Cat { Id = 2, Name = "Mac", Species = "Felis catus", EdcuationLevel = "BA" },
new Dog { Id = 3, Name = "Toast", Species = "Canis familiaris", FavoriteToy = "Mr. Squirrel" },
new FarmAnimal { Id = 4, Value = 100.0, Species = "Ovis aries" });

SaveChanges();
}
}

protected abstract class Animal
{
public int Id { get; set; }
public string Species { get; set; }
}

protected class FarmAnimal : Animal
{
public double Value { get; set; }
}

protected abstract class Pet : Animal
{
public string Name { get; set; }
}

protected class Cat : Pet
{
public string EdcuationLevel { get; set; }
}

protected class Dog : Pet
{
public string FavoriteToy { get; set; }
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -429,4 +429,46 @@ HAVING COUNT(*) = 1
WHERE [t].[TableId] = 123
ORDER BY [t].[ParcelNumber]");
}

public override async Task Hierarchy_query_with_abstract_type_sibling(bool async)
{
await base.Hierarchy_query_with_abstract_type_sibling(async);

AssertSql(
@"SELECT [a].[Id], [a].[Discriminator], [a].[Species], [a].[Name], [a].[EdcuationLevel], [a].[FavoriteToy]
FROM [Animals] AS [a]
WHERE [a].[Discriminator] IN (N'Cat', N'Dog') AND [a].[Species] IS NOT NULL AND ([a].[Species] LIKE N'F%')");
}

public override async Task Hierarchy_query_with_abstract_type_sibling_TPT(bool async)
{
await base.Hierarchy_query_with_abstract_type_sibling_TPT(async);

AssertSql(
@"SELECT [a].[Id], [a].[Species], [p].[Name], [c].[EdcuationLevel], [d].[FavoriteToy], CASE
WHEN [d].[Id] IS NOT NULL THEN N'Dog'
WHEN [c].[Id] IS NOT NULL THEN N'Cat'
END AS [Discriminator]
FROM [Animals] AS [a]
LEFT JOIN [Pets] AS [p] ON [a].[Id] = [p].[Id]
LEFT JOIN [Cats] AS [c] ON [a].[Id] = [c].[Id]
LEFT JOIN [Dogs] AS [d] ON [a].[Id] = [d].[Id]
WHERE ([d].[Id] IS NOT NULL OR [c].[Id] IS NOT NULL) AND [a].[Species] IS NOT NULL AND ([a].[Species] LIKE N'F%')");
}

public override async Task Hierarchy_query_with_abstract_type_sibling_TPC(bool async)
{
await base.Hierarchy_query_with_abstract_type_sibling_TPC(async);

AssertSql(
@"SELECT [t].[Id], [t].[Species], [t].[Name], [t].[EdcuationLevel], [t].[FavoriteToy], [t].[Discriminator]
FROM (
SELECT [c].[Id], [c].[Species], [c].[Name], [c].[EdcuationLevel], NULL AS [FavoriteToy], N'Cat' AS [Discriminator]
FROM [Cats] AS [c]
UNION ALL
SELECT [d].[Id], [d].[Species], [d].[Name], NULL AS [EdcuationLevel], [d].[FavoriteToy], N'Dog' AS [Discriminator]
FROM [Dogs] AS [d]
) AS [t]
WHERE [t].[Species] IS NOT NULL AND ([t].[Species] LIKE N'F%')");
}
}

0 comments on commit 27a83b9

Please sign in to comment.