From a60392cc674bc35a914e8fba401e04591a10a0e4 Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Wed, 24 Jul 2019 12:20:41 -0700 Subject: [PATCH] Compiled query for 3.0 Fixes #14551 --- src/EFCore/DbContext.cs | 4 +- src/EFCore/Internal/InternalDbSet.cs | 16 ++++- .../Query/Internal/CompiledAsyncTaskQuery.cs | 3 +- src/EFCore/Query/Internal/QueryCompiler.cs | 6 +- .../InMemoryComplianceTest.cs | 1 - .../Query/CompiledQueryInMemoryTest.cs | 14 ++++- .../Query/CompiledQuerySqlServerTest.cs | 59 +++---------------- .../Query/CompiledQueryInMemoryTest.cs | 2 +- 8 files changed, 41 insertions(+), 64 deletions(-) diff --git a/src/EFCore/DbContext.cs b/src/EFCore/DbContext.cs index 3a82898e844..cfebe0f4c5c 100644 --- a/src/EFCore/DbContext.cs +++ b/src/EFCore/DbContext.cs @@ -265,9 +265,9 @@ public virtual DbSet Set() /// The type of query for which a DbQuery should be returned. /// A DbQuery for the given keyless entity type. [Obsolete("Use Set() for entity types without keys")] - public virtual IQueryable Query() + public virtual DbQuery Query() where TQuery : class - => Set().AsNoTracking(); + => (DbQuery)((IDbSetCache)this).GetOrAddSet(DbContextDependencies.SetSource, typeof(TQuery)); private IEntityFinder Finder(Type type) { diff --git a/src/EFCore/Internal/InternalDbSet.cs b/src/EFCore/Internal/InternalDbSet.cs index f906d9dce8c..604bd8944a0 100644 --- a/src/EFCore/Internal/InternalDbSet.cs +++ b/src/EFCore/Internal/InternalDbSet.cs @@ -26,8 +26,9 @@ namespace Microsoft.EntityFrameworkCore.Internal /// public class InternalDbSet : #pragma warning disable CS0618 // Type or member is obsolete - DbQuery, IQueryable, IAsyncEnumerable, IInfrastructure, IResettableService + DbQuery, #pragma warning restore CS0618 // Type or member is obsolete + IQueryable, IAsyncEnumerable, IInfrastructure, IResettableService where TEntity : class { private readonly DbContext _context; @@ -110,7 +111,18 @@ private EntityQueryable EntityQueryable } private EntityQueryable CreateEntityQueryable() - => new EntityQueryable(_context.GetDependencies().QueryProvider); + { + var queryable = new EntityQueryable(_context.GetDependencies().QueryProvider); + +#pragma warning disable 618 + if (_entityType.FindPrimaryKey() == null) +#pragma warning restore 618 + { + queryable = (EntityQueryable)queryable.AsNoTracking(); + } + + return queryable; + } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore/Query/Internal/CompiledAsyncTaskQuery.cs b/src/EFCore/Query/Internal/CompiledAsyncTaskQuery.cs index 3c4d373a4a2..9fe623da7e5 100644 --- a/src/EFCore/Query/Internal/CompiledAsyncTaskQuery.cs +++ b/src/EFCore/Query/Internal/CompiledAsyncTaskQuery.cs @@ -193,7 +193,6 @@ public virtual Task ExecuteAsync protected override Func> CreateCompiledQuery( IQueryCompiler queryCompiler, Expression expression) - => throw new NotImplementedException(); - //queryCompiler.CreateCompiledAsyncSingletonQuery(expression); + => queryCompiler.CreateCompiledAsyncQuery>(expression); } } diff --git a/src/EFCore/Query/Internal/QueryCompiler.cs b/src/EFCore/Query/Internal/QueryCompiler.cs index eb7540a3645..8aa4ed5a4e6 100644 --- a/src/EFCore/Query/Internal/QueryCompiler.cs +++ b/src/EFCore/Query/Internal/QueryCompiler.cs @@ -119,8 +119,7 @@ public virtual Func CreateCompiledQuery(Expressi query = ExtractParameters(query, _queryContextFactory.Create(), _logger, parameterize: false); - //return CompileQueryCore(query, _model, _queryModelGenerator, _database, _logger, _contextType); - throw new NotImplementedException(); + return CompileQueryCore(_database, query, _model, false); } public virtual TResult ExecuteAsync(Expression query, CancellationToken cancellationToken = default) @@ -154,8 +153,7 @@ public virtual Func CreateCompiledAsyncQuery(Exp query = ExtractParameters(query, _queryContextFactory.Create(), _logger, parameterize: false); - //return CompileAsyncQueryCore(query, _queryModelGenerator, _database); - throw new NotImplementedException(); + return CompileQueryCore(_database, query, _model, true); } /// diff --git a/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs b/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs index 5d3f31603bf..854965f8126 100644 --- a/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs @@ -30,7 +30,6 @@ public class InMemoryComplianceTest : ComplianceTestBase typeof(ConcurrencyDetectorTestBase<>), typeof(AsNoTrackingTestBase<>), typeof(AsTrackingTestBase<>), - typeof(CompiledQueryTestBase<>), typeof(ComplexNavigationsQueryTestBase<>), typeof(GearsOfWarQueryTestBase<>), typeof(IncludeAsyncTestBase<>), diff --git a/test/EFCore.InMemory.FunctionalTests/Query/CompiledQueryInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/CompiledQueryInMemoryTest.cs index 4a365b942d2..029a434a050 100644 --- a/test/EFCore.InMemory.FunctionalTests/Query/CompiledQueryInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/Query/CompiledQueryInMemoryTest.cs @@ -7,13 +7,25 @@ namespace Microsoft.EntityFrameworkCore.Query { - internal class CompiledQueryInMemoryTest : CompiledQueryTestBase> + public class CompiledQueryInMemoryTest : CompiledQueryTestBase> { public CompiledQueryInMemoryTest(NorthwindQueryInMemoryFixture fixture) : base(fixture) { } + [ConditionalFact(Skip = "See issue#15318")] + public override void Query_ending_with_include() + { + base.Query_ending_with_include(); + } + + [ConditionalFact(Skip = "See issue#15318")] + public override void Query_with_single_parameter_with_include() + { + base.Query_ending_with_include(); + } + [ConditionalFact(Skip = "See issue#13857")] public override void DbQuery_query() { diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/CompiledQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/CompiledQuerySqlServerTest.cs index e633894e243..db10e8569d0 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/CompiledQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/CompiledQuerySqlServerTest.cs @@ -6,7 +6,7 @@ namespace Microsoft.EntityFrameworkCore.Query { - internal class CompiledQuerySqlServerTest : CompiledQueryTestBase> + public class CompiledQuerySqlServerTest : CompiledQueryTestBase> { public CompiledQuerySqlServerTest(NorthwindQuerySqlServerFixture fixture, ITestOutputHelper testOutputHelper) : base(fixture) @@ -42,29 +42,10 @@ public override void Query_ending_with_include() base.Query_ending_with_include(); AssertSql( - @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] -FROM [Customers] AS [c] -ORDER BY [c].[CustomerID]", - // - @"SELECT [c.Orders].[OrderID], [c.Orders].[CustomerID], [c.Orders].[EmployeeID], [c.Orders].[OrderDate] -FROM [Orders] AS [c.Orders] -INNER JOIN ( - SELECT [c0].[CustomerID] - FROM [Customers] AS [c0] -) AS [t] ON [c.Orders].[CustomerID] = [t].[CustomerID] -ORDER BY [t].[CustomerID]", - // - @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Customers] AS [c] -ORDER BY [c].[CustomerID]", - // - @"SELECT [c.Orders].[OrderID], [c.Orders].[CustomerID], [c.Orders].[EmployeeID], [c.Orders].[OrderDate] -FROM [Orders] AS [c.Orders] -INNER JOIN ( - SELECT [c0].[CustomerID] - FROM [Customers] AS [c0] -) AS [t] ON [c.Orders].[CustomerID] = [t].[CustomerID] -ORDER BY [t].[CustomerID]"); +LEFT JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +ORDER BY [c].[CustomerID], [o].[OrderID]"); } public override void Untyped_context() @@ -88,13 +69,7 @@ public override void Query_with_single_parameter() SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] = @__customerID", - // - @"@__customerID='ANATR' (Size = 5) - -SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] -FROM [Customers] AS [c] -WHERE [c].[CustomerID] = @__customerID"); +WHERE ([c].[CustomerID] = @__customerID) AND @__customerID IS NOT NULL"); } public override void First_query_with_single_parameter() @@ -106,13 +81,7 @@ public override void First_query_with_single_parameter() SELECT TOP(1) [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] = @__customerID", - // - @"@__customerID='ANATR' (Size = 5) - -SELECT TOP(1) [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] -FROM [Customers] AS [c] -WHERE [c].[CustomerID] = @__customerID"); +WHERE ([c].[CustomerID] = @__customerID) AND @__customerID IS NOT NULL"); } public override void Query_with_two_parameters() @@ -124,13 +93,7 @@ public override void Query_with_two_parameters() SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] = @__customerID", - // - @"@__customerID='ANATR' (Size = 5) - -SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] -FROM [Customers] AS [c] -WHERE [c].[CustomerID] = @__customerID"); +WHERE ([c].[CustomerID] = @__customerID) AND @__customerID IS NOT NULL"); } public override void Query_with_three_parameters() @@ -142,13 +105,7 @@ public override void Query_with_three_parameters() SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] = @__customerID", - // - @"@__customerID='ANATR' (Size = 5) - -SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] -FROM [Customers] AS [c] -WHERE [c].[CustomerID] = @__customerID"); +WHERE ([c].[CustomerID] = @__customerID) AND @__customerID IS NOT NULL"); } public override void Query_with_array_parameter() diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/CompiledQueryInMemoryTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/CompiledQueryInMemoryTest.cs index 7932703321d..a1e28de4da0 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/CompiledQueryInMemoryTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/CompiledQueryInMemoryTest.cs @@ -5,7 +5,7 @@ namespace Microsoft.EntityFrameworkCore.Query { - internal class CompiledQuerySqliteTest : CompiledQueryTestBase> + public class CompiledQuerySqliteTest : CompiledQueryTestBase> { public CompiledQuerySqliteTest(NorthwindQuerySqliteFixture fixture) : base(fixture)