From 30edaf73024c5bc5fbe563cbd12c2b8540f49bac Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Thu, 28 Oct 2021 16:17:31 -0700 Subject: [PATCH] Query: Convert IReadOnlyCollection.Count to Queryable.Count() (#26483) Resolves #26433 --- .../NavigationExpandingExpressionVisitor.cs | 5 +- .../Query/SimpleQueryTestBase.cs | 68 +++++++++++++++++++ .../Query/SimpleQuerySqlServerTest.cs | 39 +++++++++++ 3 files changed, 111 insertions(+), 1 deletion(-) diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs index 956e9b21955..deed78afc2e 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs @@ -214,7 +214,10 @@ protected override Expression VisitMember(MemberExpression memberExpression) && innerExpression != null && memberExpression.Member.Name == nameof(ICollection.Count) && memberExpression.Expression.Type.GetInterfaces().Append(memberExpression.Expression.Type) - .Any(e => e.IsGenericType && e.GetGenericTypeDefinition() == typeof(ICollection<>))) + .Any(e => e.IsGenericType + && (e.GetGenericTypeDefinition() is Type genericTypeDefinition + && (genericTypeDefinition == typeof(ICollection<>) + || genericTypeDefinition == typeof(IReadOnlyCollection<>))))) { var innerQueryable = UnwrapCollectionMaterialization(innerExpression); diff --git a/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.cs index 90db5cc757e..29f69678271 100644 --- a/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.cs @@ -324,5 +324,73 @@ public PhotoBlog() public int NumberOfPhotos { get; set; } } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Count_member_over_IReadOnlyCollection_works(bool async) + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + using var context = contextFactory.CreateContext(); + + var query = context.Authors + .Select(a => new + { + BooksCount = a.Books.Count + }); + + var authors = async + ? await query.ToListAsync() + : query.ToList(); + + Assert.Equal(3, Assert.Single(authors).BooksCount); + } + + protected class Context26433 : DbContext + { + public Context26433(DbContextOptions options) + : base(options) + { + } + + public DbSet Books { get; set; } + public DbSet Authors { get; set; } + + public void Seed() + { + + base.Add(new Author26433 + { + FirstName = "William", + LastName = "Shakespeare", + Books = new List + { + new() {Title = "Hamlet"}, + new() {Title = "Othello"}, + new() {Title = "MacBeth"} + } + }); + + SaveChanges(); + } + } + + protected class Author26433 + { + [Key] + public int AuthorId { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public IReadOnlyCollection Books { get; set; } + } + + protected class Book26433 + { + [Key] + public int BookId { get; set; } + public string Title { get; set; } + public int AuthorId { get; set; } + public Author26433 Author { get; set; } + } + } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.cs index 4fad38a5246..2a6dacbf857 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.cs @@ -63,5 +63,44 @@ public override async Task Comparing_byte_column_to_enum_in_vb_creating_double_c FROM [Food] AS [f] WHERE [f].[Taste] = CAST(1 AS tinyint)"); } + + public override async Task Null_check_removal_in_ternary_maintain_appropriate_cast(bool async) + { + await base.Null_check_removal_in_ternary_maintain_appropriate_cast(async); + + AssertSql( + @"SELECT CAST([f].[Taste] AS tinyint) AS [Bar] +FROM [Food] AS [f]"); + } + + public override async Task Bool_discriminator_column_works(bool async) + { + await base.Bool_discriminator_column_works(async); + + AssertSql( + @"SELECT [a].[Id], [a].[BlogId], [b].[Id], [b].[IsPhotoBlog], [b].[Title], [b].[NumberOfPhotos] +FROM [Authors] AS [a] +LEFT JOIN [Blog] AS [b] ON [a].[BlogId] = [b].[Id]"); + } + + public override async Task Count_member_over_IReadOnlyCollection_works(bool async) + { + await base.Count_member_over_IReadOnlyCollection_works(async); + + AssertSql( + @"SELECT ( + SELECT COUNT(*) + FROM [Books] AS [b] + WHERE [a].[AuthorId] = [b].[AuthorId]) AS [BooksCount] +FROM [Authors] AS [a]"); + } + + public override async Task Multiple_different_entity_type_from_different_namespaces(bool async) + { + await base.Multiple_different_entity_type_from_different_namespaces(async); + + AssertSql( + @"SELECT cast(null as int) AS MyValue"); + } } }