From f414e637f3527967924f06d06fdea7f1073ae6da Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Wed, 14 Feb 2018 11:25:20 -0800 Subject: [PATCH] Query: Allow entities which are table splitting with derived type DRY code to generate discriminator for a query for materialize & non-materialized case Find identifying relationship chain to root and add discriminator of relavent types Resolves #8973 --- .../TableSplittingTestBase.cs | 17 +- .../RelationalModelValidator.cs | 29 +-- .../Properties/RelationalStrings.Designer.cs | 8 - .../Properties/RelationalStrings.resx | 3 - .../Internal/IMaterializerFactory.cs | 2 - .../Internal/MaterializerFactory.cs | 35 +--- ...ationalEntityQueryableExpressionVisitor.cs | 152 +++++++++++--- .../Query/OwnedQueryTestBase.cs | 2 +- .../TransportationContext.cs | 7 + temp.txt | Bin 41768 -> 0 bytes .../RelationalModelValidatorTest.cs | 15 -- .../Query/InheritanceSqlServerTest.cs | 2 +- .../Query/OwnedQuerySqlServerTest.cs | 187 ++++++++++++++++-- .../TableSplittingSqlServerTest.cs | 30 ++- .../Query/OwnedQuerySqliteTest.cs | 36 ---- 15 files changed, 349 insertions(+), 176 deletions(-) delete mode 100644 temp.txt diff --git a/src/EFCore.Relational.Specification.Tests/TableSplittingTestBase.cs b/src/EFCore.Relational.Specification.Tests/TableSplittingTestBase.cs index 1a8872b5a82..40cdbbf05b3 100644 --- a/src/EFCore.Relational.Specification.Tests/TableSplittingTestBase.cs +++ b/src/EFCore.Relational.Specification.Tests/TableSplittingTestBase.cs @@ -21,7 +21,7 @@ protected TableSplittingTestBase(ITestOutputHelper testOutputHelper) TestOutputHelper = testOutputHelper; } - [Fact(Skip = "#8973")] + [Fact] public virtual void Can_query_shared() { using (CreateTestStore(OnModelCreating)) @@ -33,7 +33,7 @@ public virtual void Can_query_shared() } } - [Fact(Skip = "#8973")] + [Fact] public virtual void Can_query_shared_derived() { using (CreateTestStore(OnModelCreating)) @@ -45,13 +45,13 @@ public virtual void Can_query_shared_derived() } } - [Fact(Skip = "#8973")] + [Fact] public virtual void Can_use_with_redundant_relationships() { Test_roundtrip(OnModelCreating); } - [Fact(Skip = "#8973")] + [Fact] public virtual void Can_use_with_chained_relationships() { Test_roundtrip( @@ -62,7 +62,7 @@ public virtual void Can_use_with_chained_relationships() }); } - [Fact(Skip = "#8973")] + [Fact(Skip = "Issue#10971")] public virtual void Can_use_with_fanned_relationships() { Test_roundtrip( @@ -181,6 +181,8 @@ public virtual void Can_change_principal_instance_non_derived() protected void AssertSql(params string[] expected) => TestSqlLoggerFactory.AssertBaseline(expected); + protected void AssertContainsSql(params string[] expected) + => TestSqlLoggerFactory.AssertBaseline(expected, assertOrder: false); protected virtual void OnModelCreating(ModelBuilder modelBuilder) { @@ -207,6 +209,11 @@ protected TestStore CreateTestStore(Action onModelCreating) TestStore.Initialize(ServiceProvider, CreateContext, c => ((TransportationContext)c).Seed()); + TestSqlLoggerFactory.Clear(); + + // To enable logging + //TestSqlLoggerFactory.SetTestOutputHelper(TestOutputHelper); + return TestStore; } diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs index 417aff76ee1..8faa32eb1ed 100644 --- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs +++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs @@ -266,30 +266,11 @@ protected virtual void ValidateSharedTableCompatibility( } } - private static bool IsIdentifyingPrincipal(IEntityType dependEntityType, IEntityType principalEntityType) + private static bool IsIdentifyingPrincipal(IEntityType dependentEntityType, IEntityType principalEntityType) { - foreach (var foreignKey in dependEntityType.FindForeignKeys(dependEntityType.FindPrimaryKey().Properties)) - { - if (!foreignKey.PrincipalKey.IsPrimaryKey() - || foreignKey.PrincipalEntityType != principalEntityType) - { - continue; - } - - if (principalEntityType.BaseType != null) - { - // #8973 - throw new InvalidOperationException( - RelationalStrings.IncompatibleTableDerivedPrincipal( - dependEntityType.Relational().TableName, - dependEntityType.DisplayName(), - principalEntityType.DisplayName(), - principalEntityType.RootType().DisplayName())); - } - return true; - } - - return false; + return dependentEntityType.FindForeignKeys(dependentEntityType.FindPrimaryKey().Properties) + .Any(fk => fk.PrincipalKey.IsPrimaryKey() + && fk.PrincipalEntityType == principalEntityType); } /// @@ -588,7 +569,7 @@ protected virtual void ValidateInheritanceMapping([NotNull] IModel model) } } - private void ValidateDiscriminator(IEntityType entityType) + private static void ValidateDiscriminator(IEntityType entityType) { var annotations = entityType.Relational(); if (annotations.DiscriminatorProperty == null) diff --git a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs index f9661d01fe3..dd0632c8650 100644 --- a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs +++ b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs @@ -468,14 +468,6 @@ public static string IncompatibleTableNoRelationship([CanBeNull] object table, [ GetString("IncompatibleTableNoRelationship", nameof(table), nameof(entityType), nameof(otherEntityType)), table, entityType, otherEntityType); - /// - /// Cannot use table '{table}' for entity type '{dependentType}' since it has a relationship to a derived entity type '{principalType}'. Either point the relationship to the base type '{rootType}' or map '{dependentType}' to a different table. - /// - public static string IncompatibleTableDerivedPrincipal([CanBeNull] object table, [CanBeNull] object dependentType, [CanBeNull] object principalType, [CanBeNull] object rootType) - => string.Format( - GetString("IncompatibleTableDerivedPrincipal", nameof(table), nameof(dependentType), nameof(principalType), nameof(rootType)), - table, dependentType, principalType, rootType); - /// /// Property '{property}' on entity type '{entityType}' is part of a primary or alternate key but has a constant default value set. Constant default values are not useful for primary or alternate keys since these properties must always have non-null unqiue values. /// diff --git a/src/EFCore.Relational/Properties/RelationalStrings.resx b/src/EFCore.Relational/Properties/RelationalStrings.resx index 1f7f4417bd4..6ab6ae001b9 100644 --- a/src/EFCore.Relational/Properties/RelationalStrings.resx +++ b/src/EFCore.Relational/Properties/RelationalStrings.resx @@ -284,9 +284,6 @@ Cannot use table '{table}' for entity type '{entityType}' since it is being used for entity type '{otherEntityType}' and there is no relationship between their primary keys. - - Cannot use table '{table}' for entity type '{dependentType}' since it has a relationship to a derived entity type '{principalType}'. Either point the relationship to the base type '{rootType}' or map '{dependentType}' to a different table. - Property '{property}' on entity type '{entityType}' is part of a primary or alternate key but has a constant default value set. Constant default values are not useful for primary or alternate keys since these properties must always have non-null unqiue values. Warning RelationalEventId.ModelValidationKeyDefaultValueWarning string string diff --git a/src/EFCore.Relational/Query/ExpressionVisitors/Internal/IMaterializerFactory.cs b/src/EFCore.Relational/Query/ExpressionVisitors/Internal/IMaterializerFactory.cs index af088c058b5..8c143654f4c 100644 --- a/src/EFCore.Relational/Query/ExpressionVisitors/Internal/IMaterializerFactory.cs +++ b/src/EFCore.Relational/Query/ExpressionVisitors/Internal/IMaterializerFactory.cs @@ -8,7 +8,6 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Query.Expressions; using Microsoft.EntityFrameworkCore.Storage; -using Remotion.Linq.Clauses; namespace Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal { @@ -26,7 +25,6 @@ Expression> CreateMaterializer( [NotNull] IEntityType entityType, [NotNull] SelectExpression selectExpression, [NotNull] Func projectionAdder, - [CanBeNull] IQuerySource querySource, out Dictionary typeIndexMap); } } diff --git a/src/EFCore.Relational/Query/ExpressionVisitors/Internal/MaterializerFactory.cs b/src/EFCore.Relational/Query/ExpressionVisitors/Internal/MaterializerFactory.cs index 0d49d18b1a6..0530b272617 100644 --- a/src/EFCore.Relational/Query/ExpressionVisitors/Internal/MaterializerFactory.cs +++ b/src/EFCore.Relational/Query/ExpressionVisitors/Internal/MaterializerFactory.cs @@ -13,7 +13,6 @@ using Microsoft.EntityFrameworkCore.Query.Expressions; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Utilities; -using Remotion.Linq.Clauses; namespace Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal { @@ -45,7 +44,6 @@ public virtual Expression> CreateMaterializ IEntityType entityType, SelectExpression selectExpression, Func projectionAdder, - IQuerySource querySource, out Dictionary typeIndexMap) { Check.NotNull(entityType, nameof(entityType)); @@ -59,7 +57,7 @@ public virtual Expression> CreateMaterializ var rootEntityType = concreteEntityTypes[0]; var indexMap = new int[rootEntityType.PropertyCount()]; var contextParameter = Expression.Parameter(typeof(DbContext), "context"); - + foreach (var property in rootEntityType.GetProperties()) { indexMap[property.GetIndex()] = projectionAdder(property, selectExpression); @@ -70,8 +68,7 @@ var materializer .CreateMaterializeExpression( rootEntityType, valueBufferParameter, contextParameter, indexMap); - if (concreteEntityTypes.Count == 1 - && rootEntityType.RootType() == rootEntityType) + if (concreteEntityTypes.Count == 1) { return Expression.Lambda>( materializer, valueBufferParameter, contextParameter); @@ -79,26 +76,10 @@ var materializer var discriminatorProperty = rootEntityType.Relational().DiscriminatorProperty; - var discriminatorColumn - = selectExpression.Projection - .Last(c => (c as ColumnExpression)?.Property == discriminatorProperty); - var firstDiscriminatorValue = Expression.Constant( rootEntityType.Relational().DiscriminatorValue, - discriminatorColumn.Type); - - var discriminatorPredicate - = Expression.Equal(discriminatorColumn, firstDiscriminatorValue); - - if (concreteEntityTypes.Count == 1) - { - selectExpression.Predicate - = new DiscriminatorPredicateExpression(discriminatorPredicate, querySource); - - return Expression.Lambda>( - materializer, valueBufferParameter, contextParameter); - } + discriminatorProperty.ClrType); var discriminatorValueVariable = Expression.Variable(discriminatorProperty.ClrType); @@ -154,7 +135,7 @@ var blockExpressions var discriminatorValue = Expression.Constant( concreteEntityType.Relational().DiscriminatorValue, - discriminatorColumn.Type); + discriminatorProperty.ClrType); materializer = _entityMaterializerSource @@ -166,16 +147,8 @@ var discriminatorValue Expression.Equal(discriminatorValueVariable, discriminatorValue), Expression.Return(returnLabelTarget, materializer), blockExpressions[1]); - - discriminatorPredicate - = Expression.OrElse( - Expression.Equal(discriminatorColumn, discriminatorValue), - discriminatorPredicate); } - selectExpression.Predicate - = new DiscriminatorPredicateExpression(discriminatorPredicate, querySource); - return Expression.Lambda>( Expression.Block(new[] { discriminatorValueVariable }, blockExpressions), valueBufferParameter, diff --git a/src/EFCore.Relational/Query/ExpressionVisitors/RelationalEntityQueryableExpressionVisitor.cs b/src/EFCore.Relational/Query/ExpressionVisitors/RelationalEntityQueryableExpressionVisitor.cs index 2a3a86049b0..6f685bbd722 100644 --- a/src/EFCore.Relational/Query/ExpressionVisitors/RelationalEntityQueryableExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/ExpressionVisitors/RelationalEntityQueryableExpressionVisitor.cs @@ -227,8 +227,10 @@ var useQueryComposition var shaper = CreateShaper(elementType, entityType, selectExpression); + DiscriminateProjectionQuery(entityType, selectExpression, _querySource); + return Expression.Call( - QueryModelVisitor.QueryCompilationContext.QueryMethodProvider // TODO: Don't use ShapedQuery when projecting + QueryModelVisitor.QueryCompilationContext.QueryMethodProvider .ShapedQueryMethod .MakeGenericMethod(shaper.Type), EntityQueryModelVisitor.QueryContextParameter, @@ -253,7 +255,6 @@ var materializerExpression se.AddToProjection( p, _querySource), - _querySource, out var typeIndexMap); var materializer = materializerExpression.Compile(); @@ -276,15 +277,79 @@ var materializerExpression } else { - DiscriminateProjectionQuery(entityType, selectExpression, _querySource); - shaper = new ValueBufferShaper(_querySource); } return shaper; } - private void DiscriminateProjectionQuery( + private List FindIdentifyingPrincipalChain(IEntityType entityType, SelectExpression selectExpression) + { + var tableName = entityType.Relational().TableName; + var tableSchema = entityType.Relational().Schema; + + var sharedTypes = _model.GetEntityTypes() + .Where(et => !et.IsQueryType) + // Because all derived types are mapped to same table + .Where(et => et.BaseType == null) + .Where(et => et.Relational().TableName == tableName + && et.Relational().Schema == tableSchema); + + var connectedTypes = new HashSet(sharedTypes); + + var root = sharedTypes.Single( + t => t.FindForeignKeys(t.FindPrimaryKey().Properties) + .All(fk => !fk.PrincipalKey.IsPrimaryKey() + || fk.PrincipalEntityType.RootType() == t + || !connectedTypes.Contains(fk.PrincipalEntityType.RootType()))); + + connectedTypes.Remove(root); + var typesToVisit = new Queue(); + typesToVisit.Enqueue(root); + + var pathInfos = new Dictionary + { + { root, (Weight: 0, Path: null) } + }; + + while (typesToVisit.Count > 0) + { + var currentEntityType = typesToVisit.Dequeue(); + var currentWeight = pathInfos[currentEntityType].Weight; + + foreach (var nextEntityType in connectedTypes) + { + var identifyingFk = nextEntityType + .FindForeignKeys(nextEntityType.FindPrimaryKey().Properties) + .FirstOrDefault(fk => fk.PrincipalKey.IsPrimaryKey() + && fk.PrincipalEntityType.RootType() == currentEntityType); + + if (identifyingFk == null) + { + continue; + } + + typesToVisit.Enqueue(nextEntityType); + + if (!pathInfos.TryGetValue(nextEntityType, out var pathInfo) + || pathInfo.Weight < currentWeight + 1) + { + pathInfos[nextEntityType] = (currentWeight + 1, identifyingFk); + } + } + } + + var list = new List(); + while (entityType != null) + { + list.Add(entityType); + entityType = pathInfos[entityType.RootType()].Path?.PrincipalEntityType; + } + + return list; + } + + private static Expression GenerateDiscriminatorExpression( IEntityType entityType, SelectExpression selectExpression, IQuerySource querySource) { var concreteEntityTypes @@ -293,15 +358,12 @@ var concreteEntityTypes if (concreteEntityTypes.Count == 1 && concreteEntityTypes[0].RootType() == concreteEntityTypes[0]) { - return; + return null; } - var discriminatorProperty - = concreteEntityTypes[0].Relational().DiscriminatorProperty; - var discriminatorColumn = selectExpression.BindProperty( - discriminatorProperty, + concreteEntityTypes[0].Relational().DiscriminatorProperty, querySource); var firstDiscriminatorValue @@ -312,30 +374,60 @@ var firstDiscriminatorValue var discriminatorPredicate = Expression.Equal(discriminatorColumn, firstDiscriminatorValue); - if (concreteEntityTypes.Count == 1) + if (concreteEntityTypes.Count > 1) { - selectExpression.Predicate - = new DiscriminatorPredicateExpression(discriminatorPredicate, querySource); + discriminatorPredicate + = concreteEntityTypes + .Skip(1) + .Select( + concreteEntityType + => Expression.Constant( + concreteEntityType.Relational().DiscriminatorValue, + discriminatorColumn.Type)) + .Aggregate( + discriminatorPredicate, (current, discriminatorValue) => + Expression.OrElse( + Expression.Equal(discriminatorColumn, discriminatorValue), + current)); + - return; } - discriminatorPredicate - = concreteEntityTypes - .Skip(1) - .Select( - concreteEntityType - => Expression.Constant( - concreteEntityType.Relational().DiscriminatorValue, - discriminatorColumn.Type)) - .Aggregate( - discriminatorPredicate, (current, discriminatorValue) => - Expression.OrElse( - Expression.Equal(discriminatorColumn, discriminatorValue), - current)); - - selectExpression.Predicate - = new DiscriminatorPredicateExpression(discriminatorPredicate, querySource); + return discriminatorPredicate; + } + + private void DiscriminateProjectionQuery( + IEntityType entityType, SelectExpression selectExpression, IQuerySource querySource) + { + Expression discriminatorPredicate = null; + + if (entityType.IsQueryType) + { + discriminatorPredicate = GenerateDiscriminatorExpression(entityType, selectExpression, querySource); + } + else + { + var identifyingPrincipalChain = FindIdentifyingPrincipalChain(entityType, selectExpression); + + discriminatorPredicate + = identifyingPrincipalChain.Count == 1 + ? GenerateDiscriminatorExpression(entityType, selectExpression, querySource) + : identifyingPrincipalChain + .Select( + et => GenerateDiscriminatorExpression(et, selectExpression, querySource)) + .Aggregate( + (Expression)null, + (result, current) => result != null + ? current != null + ? Expression.AndAlso(result, current) + : result + : current); + } + + if (discriminatorPredicate != null) + { + selectExpression.Predicate = new DiscriminatorPredicateExpression(discriminatorPredicate, querySource); + } } private static readonly MethodInfo _createEntityShaperMethodInfo diff --git a/src/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs b/src/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs index 3aa46ead8d9..63f7b62202a 100644 --- a/src/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs +++ b/src/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs @@ -68,7 +68,7 @@ public virtual void Query_for_leaf_type_loads_all_owned_navs() } } - [Fact] + [Fact(Skip = "Issue#10971")] public virtual void Query_when_group_by() { using (var context = CreateContext()) diff --git a/src/EFCore.Specification.Tests/TestModels/TransportationModel/TransportationContext.cs b/src/EFCore.Specification.Tests/TestModels/TransportationModel/TransportationContext.cs index dd6ea2dd79f..24866a3ff5f 100644 --- a/src/EFCore.Specification.Tests/TestModels/TransportationModel/TransportationContext.cs +++ b/src/EFCore.Specification.Tests/TestModels/TransportationModel/TransportationContext.cs @@ -56,6 +56,13 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) public void Seed() { Vehicles.AddRange(CreateVehicles()); + // Following should throw + //Add(new FuelTank + //{ + // Capacity = "Unknown", + // FuelType = "Unknown", + // VehicleName = "1984 California Car" + //}); SaveChanges(); } diff --git a/temp.txt b/temp.txt deleted file mode 100644 index 0ea4593c863edaa03359bef436d23c184a3fa5cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41768 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.HasOne().WithOne().IsRequired().HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id); - modelBuilder.Entity().HasBaseType().ToTable("Table"); - modelBuilder.Entity().ToTable("Table"); - modelBuilder.Entity().ToTable("Table"); - - VerifyError( - RelationalStrings.IncompatibleTableDerivedPrincipal( - "Table", nameof(B), nameof(D), nameof(A)), modelBuilder.Model); - } - [Fact] public virtual void Passes_for_compatible_shared_table() { diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceSqlServerTest.cs index 1dbaf7f1de0..e1b868a3026 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceSqlServerTest.cs @@ -208,7 +208,7 @@ FROM [Animal] AS [a] WHERE [a].[Discriminator] IN (N'Kiwi', N'Eagle') ORDER BY [a].[Species]"); } - + public override void Can_query_all_animal_views() { base.Can_query_all_animal_views(); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/OwnedQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/OwnedQuerySqlServerTest.cs index bee8b2a7d2f..e1e16924989 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/OwnedQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/OwnedQuerySqlServerTest.cs @@ -16,50 +16,209 @@ public OwnedQuerySqlServerTest(OwnedQuerySqlServerFixture fixture) fixture.TestSqlLoggerFactory.Clear(); } - - [Fact(Skip = "#8973")] - public override void No_ignored_include_warning_when_implicit_load() + public override void Query_for_base_type_loads_all_owned_navs() { - base.No_ignored_include_warning_when_implicit_load(); + base.Query_for_base_type_loads_all_owned_navs(); + + + // See issue#10067 + AssertSql( + @"SELECT [o].[Id], [o].[Discriminator], [t].[Id], [t0].[Id], [t0].[LeafBAddress_Country_Name], [t1].[Id], [t2].[Id], [t2].[LeafAAddress_Country_Name], [t3].[Id], [t4].[Id], [t4].[BranchAddress_Country_Name], [t5].[Id], [t6].[Id], [t6].[PersonAddress_Country_Name] +FROM [OwnedPerson] AS [o] +LEFT JOIN ( + SELECT [l.LeafBAddress].* + FROM [OwnedPerson] AS [l.LeafBAddress] + WHERE [l.LeafBAddress].[Discriminator] = N'LeafB' +) AS [t] ON [o].[Id] = [t].[Id] +LEFT JOIN ( + SELECT [l.LeafBAddress.Country].* + FROM [OwnedPerson] AS [l.LeafBAddress.Country] + WHERE [l.LeafBAddress.Country].[Discriminator] = N'LeafB' +) AS [t0] ON [t].[Id] = [t0].[Id] +LEFT JOIN ( + SELECT [l.LeafAAddress].* + FROM [OwnedPerson] AS [l.LeafAAddress] + WHERE [l.LeafAAddress].[Discriminator] = N'LeafA' +) AS [t1] ON [o].[Id] = [t1].[Id] +LEFT JOIN ( + SELECT [l.LeafAAddress.Country].* + FROM [OwnedPerson] AS [l.LeafAAddress.Country] + WHERE [l.LeafAAddress.Country].[Discriminator] = N'LeafA' +) AS [t2] ON [t1].[Id] = [t2].[Id] +LEFT JOIN ( + SELECT [b.BranchAddress].* + FROM [OwnedPerson] AS [b.BranchAddress] + WHERE [b.BranchAddress].[Discriminator] IN (N'LeafA', N'Branch') +) AS [t3] ON [o].[Id] = [t3].[Id] +LEFT JOIN ( + SELECT [b.BranchAddress.Country].* + FROM [OwnedPerson] AS [b.BranchAddress.Country] + WHERE [b.BranchAddress.Country].[Discriminator] IN (N'LeafA', N'Branch') +) AS [t4] ON [t3].[Id] = [t4].[Id] +LEFT JOIN ( + SELECT [o.PersonAddress].* + FROM [OwnedPerson] AS [o.PersonAddress] + WHERE [o.PersonAddress].[Discriminator] IN (N'LeafB', N'LeafA', N'Branch', N'OwnedPerson') +) AS [t5] ON [o].[Id] = [t5].[Id] +LEFT JOIN ( + SELECT [o.PersonAddress.Country].* + FROM [OwnedPerson] AS [o.PersonAddress.Country] + WHERE [o.PersonAddress.Country].[Discriminator] IN (N'LeafB', N'LeafA', N'Branch', N'OwnedPerson') +) AS [t6] ON [t5].[Id] = [t6].[Id] +WHERE [o].[Discriminator] IN (N'LeafB', N'LeafA', N'Branch', N'OwnedPerson')"); } - [Fact(Skip = "#8973")] - public override void Query_for_base_type_loads_all_owned_navs() + public override void No_ignored_include_warning_when_implicit_load() { - base.Query_for_base_type_loads_all_owned_navs(); + base.No_ignored_include_warning_when_implicit_load(); - AssertSql(""); + AssertSql( + @"SELECT COUNT(*) +FROM [OwnedPerson] AS [o] +WHERE [o].[Discriminator] IN (N'LeafB', N'LeafA', N'Branch', N'OwnedPerson')"); } - [Fact(Skip = "#8973")] public override void Query_for_branch_type_loads_all_owned_navs() { base.Query_for_branch_type_loads_all_owned_navs(); - AssertSql(""); + AssertSql( + @"SELECT [o].[Id], [o].[Discriminator], [t].[Id], [t0].[Id], [t0].[LeafAAddress_Country_Name], [t1].[Id], [t2].[Id], [t2].[BranchAddress_Country_Name], [t3].[Id], [t4].[Id], [t4].[PersonAddress_Country_Name] +FROM [OwnedPerson] AS [o] +LEFT JOIN ( + SELECT [l.LeafAAddress].* + FROM [OwnedPerson] AS [l.LeafAAddress] + WHERE [l.LeafAAddress].[Discriminator] = N'LeafA' +) AS [t] ON [o].[Id] = [t].[Id] +LEFT JOIN ( + SELECT [l.LeafAAddress.Country].* + FROM [OwnedPerson] AS [l.LeafAAddress.Country] + WHERE [l.LeafAAddress.Country].[Discriminator] = N'LeafA' +) AS [t0] ON [t].[Id] = [t0].[Id] +LEFT JOIN ( + SELECT [b.BranchAddress].* + FROM [OwnedPerson] AS [b.BranchAddress] + WHERE [b.BranchAddress].[Discriminator] IN (N'LeafA', N'Branch') +) AS [t1] ON [o].[Id] = [t1].[Id] +LEFT JOIN ( + SELECT [b.BranchAddress.Country].* + FROM [OwnedPerson] AS [b.BranchAddress.Country] + WHERE [b.BranchAddress.Country].[Discriminator] IN (N'LeafA', N'Branch') +) AS [t2] ON [t1].[Id] = [t2].[Id] +LEFT JOIN ( + SELECT [o.PersonAddress].* + FROM [OwnedPerson] AS [o.PersonAddress] + WHERE [o.PersonAddress].[Discriminator] IN (N'LeafB', N'LeafA', N'Branch', N'OwnedPerson') +) AS [t3] ON [o].[Id] = [t3].[Id] +LEFT JOIN ( + SELECT [o.PersonAddress.Country].* + FROM [OwnedPerson] AS [o.PersonAddress.Country] + WHERE [o.PersonAddress.Country].[Discriminator] IN (N'LeafB', N'LeafA', N'Branch', N'OwnedPerson') +) AS [t4] ON [t3].[Id] = [t4].[Id] +WHERE [o].[Discriminator] IN (N'LeafA', N'Branch')"); } - [Fact(Skip = "#8973")] public override void Query_for_leaf_type_loads_all_owned_navs() { base.Query_for_leaf_type_loads_all_owned_navs(); AssertSql( - @"SELECT [o].[Id], [o].[Discriminator], [o].[Id], [o].[Id], [o].[LeafAAddress_Country_Name], [o].[Id], [o].[Id], [o].[BranchAddress_Country_Name], [o].[Id], [o].[Id], [o].[PersonAddress_Country_Name] + @"SELECT [o].[Id], [o].[Discriminator], [t].[Id], [t0].[Id], [t0].[LeafAAddress_Country_Name], [t1].[Id], [t2].[Id], [t2].[BranchAddress_Country_Name], [t3].[Id], [t4].[Id], [t4].[PersonAddress_Country_Name] FROM [OwnedPerson] AS [o] +LEFT JOIN ( + SELECT [l.LeafAAddress].* + FROM [OwnedPerson] AS [l.LeafAAddress] + WHERE [l.LeafAAddress].[Discriminator] = N'LeafA' +) AS [t] ON [o].[Id] = [t].[Id] +LEFT JOIN ( + SELECT [l.LeafAAddress.Country].* + FROM [OwnedPerson] AS [l.LeafAAddress.Country] + WHERE [l.LeafAAddress.Country].[Discriminator] = N'LeafA' +) AS [t0] ON [t].[Id] = [t0].[Id] +LEFT JOIN ( + SELECT [b.BranchAddress].* + FROM [OwnedPerson] AS [b.BranchAddress] + WHERE [b.BranchAddress].[Discriminator] IN (N'LeafA', N'Branch') +) AS [t1] ON [o].[Id] = [t1].[Id] +LEFT JOIN ( + SELECT [b.BranchAddress.Country].* + FROM [OwnedPerson] AS [b.BranchAddress.Country] + WHERE [b.BranchAddress.Country].[Discriminator] IN (N'LeafA', N'Branch') +) AS [t2] ON [t1].[Id] = [t2].[Id] +LEFT JOIN ( + SELECT [o.PersonAddress].* + FROM [OwnedPerson] AS [o.PersonAddress] + WHERE [o.PersonAddress].[Discriminator] IN (N'LeafB', N'LeafA', N'Branch', N'OwnedPerson') +) AS [t3] ON [o].[Id] = [t3].[Id] +LEFT JOIN ( + SELECT [o.PersonAddress.Country].* + FROM [OwnedPerson] AS [o.PersonAddress.Country] + WHERE [o.PersonAddress.Country].[Discriminator] IN (N'LeafB', N'LeafA', N'Branch', N'OwnedPerson') +) AS [t4] ON [t3].[Id] = [t4].[Id] WHERE [o].[Discriminator] = N'LeafA'"); } - [Fact(Skip = "#8973")] public override void Query_when_group_by() { base.Query_when_group_by(); + + AssertSql(" "); } - [Fact(Skip = "#8973")] public override void Query_when_subquery() { base.Query_when_subquery(); + + AssertSql( + @"@__p_0='5' + +SELECT TOP(@__p_0) [t].[Id], [t].[Discriminator], [t0].[Id], [t1].[Id], [t1].[LeafBAddress_Country_Name], [t2].[Id], [t3].[Id], [t3].[LeafAAddress_Country_Name], [t4].[Id], [t5].[Id], [t5].[BranchAddress_Country_Name], [t6].[Id], [t7].[Id], [t7].[PersonAddress_Country_Name] +FROM ( + SELECT DISTINCT [o].[Id], [o].[Discriminator] + FROM [OwnedPerson] AS [o] + WHERE [o].[Discriminator] IN (N'LeafB', N'LeafA', N'Branch', N'OwnedPerson') +) AS [t] +LEFT JOIN ( + SELECT [p.LeafBAddress].* + FROM [OwnedPerson] AS [p.LeafBAddress] + WHERE [p.LeafBAddress].[Discriminator] = N'LeafB' +) AS [t0] ON [t].[Id] = [t0].[Id] +LEFT JOIN ( + SELECT [p.LeafBAddress.Country].* + FROM [OwnedPerson] AS [p.LeafBAddress.Country] + WHERE [p.LeafBAddress.Country].[Discriminator] = N'LeafB' +) AS [t1] ON [t0].[Id] = [t1].[Id] +LEFT JOIN ( + SELECT [p.LeafAAddress].* + FROM [OwnedPerson] AS [p.LeafAAddress] + WHERE [p.LeafAAddress].[Discriminator] = N'LeafA' +) AS [t2] ON [t].[Id] = [t2].[Id] +LEFT JOIN ( + SELECT [p.LeafAAddress.Country].* + FROM [OwnedPerson] AS [p.LeafAAddress.Country] + WHERE [p.LeafAAddress.Country].[Discriminator] = N'LeafA' +) AS [t3] ON [t2].[Id] = [t3].[Id] +LEFT JOIN ( + SELECT [p.BranchAddress].* + FROM [OwnedPerson] AS [p.BranchAddress] + WHERE [p.BranchAddress].[Discriminator] IN (N'LeafA', N'Branch') +) AS [t4] ON [t].[Id] = [t4].[Id] +LEFT JOIN ( + SELECT [p.BranchAddress.Country].* + FROM [OwnedPerson] AS [p.BranchAddress.Country] + WHERE [p.BranchAddress.Country].[Discriminator] IN (N'LeafA', N'Branch') +) AS [t5] ON [t4].[Id] = [t5].[Id] +LEFT JOIN ( + SELECT [p.PersonAddress].* + FROM [OwnedPerson] AS [p.PersonAddress] + WHERE [p.PersonAddress].[Discriminator] IN (N'LeafB', N'LeafA', N'Branch', N'OwnedPerson') +) AS [t6] ON [t].[Id] = [t6].[Id] +LEFT JOIN ( + SELECT [p.PersonAddress.Country].* + FROM [OwnedPerson] AS [p.PersonAddress.Country] + WHERE [p.PersonAddress.Country].[Discriminator] IN (N'LeafB', N'LeafA', N'Branch', N'OwnedPerson') +) AS [t7] ON [t6].[Id] = [t7].[Id] +ORDER BY [t].[Id]"); } private void AssertSql(params string[] expected) diff --git a/test/EFCore.SqlServer.FunctionalTests/TableSplittingSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/TableSplittingSqlServerTest.cs index 3b2ad358859..19af67a09b4 100644 --- a/test/EFCore.SqlServer.FunctionalTests/TableSplittingSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/TableSplittingSqlServerTest.cs @@ -15,11 +15,31 @@ public TableSplittingSqlServerTest(ITestOutputHelper testOutputHelper) protected override ITestStoreFactory TestStoreFactory => SqlServerTestStoreFactory.Instance; + public override void Can_query_shared() + { + base.Can_query_shared(); + + AssertSql( + @"SELECT [v].[Name], [v].[Operator_Discriminator], [v].[Operator_Name], [v].[LicenseType] +FROM [Vehicles] AS [v] +WHERE [v].[Operator_Discriminator] IN (N'LicensedOperator', N'Operator') AND [v].[Discriminator] IN (N'PoweredVehicle', N'Vehicle')"); + } + + public override void Can_query_shared_derived() + { + base.Can_query_shared_derived(); + + AssertSql( + @"SELECT [v].[Name], [v].[Capacity], [v].[FuelType] +FROM [Vehicles] AS [v] +WHERE ([v].[Engine_Discriminator] = N'CombustionEngine') AND ([v].[Discriminator] = N'PoweredVehicle')"); + } + public override void Can_change_dependent_instance_non_derived() { base.Can_change_dependent_instance_non_derived(); - TestSqlLoggerFactory.AssertBaseline(new []{ + AssertContainsSql( @"@p3='Trek Pro Fit Madone 6 Series' (Nullable = false) (Size = 450) @p0='LicensedOperator' (Nullable = false) (Size = 4000) @p1='repairman' (Size = 4000) @@ -28,23 +48,21 @@ public override void Can_change_dependent_instance_non_derived() SET NOCOUNT ON; UPDATE [Vehicles] SET [Operator_Discriminator] = @p0, [Operator_Name] = @p1, [LicenseType] = @p2 WHERE [Name] = @p3; -SELECT @@ROWCOUNT;" - }, assertOrder: false); +SELECT @@ROWCOUNT;"); } public override void Can_change_principal_instance_non_derived() { base.Can_change_principal_instance_non_derived(); - TestSqlLoggerFactory.AssertBaseline(new[]{ + AssertContainsSql( @"@p1='Trek Pro Fit Madone 6 Series' (Nullable = false) (Size = 450) @p0='2' SET NOCOUNT ON; UPDATE [Vehicles] SET [SeatingCapacity] = @p0 WHERE [Name] = @p1; -SELECT @@ROWCOUNT;" - }, assertOrder: false); +SELECT @@ROWCOUNT;"); } } } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/OwnedQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/OwnedQuerySqliteTest.cs index d7380ad0a9f..0b15be8dc25 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/OwnedQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/OwnedQuerySqliteTest.cs @@ -15,42 +15,6 @@ public OwnedQuerySqliteTest(OwnedQuerySqliteFixture fixture) { } - [Fact(Skip = "#8973")] - public override void No_ignored_include_warning_when_implicit_load() - { - base.No_ignored_include_warning_when_implicit_load(); - } - - [Fact(Skip = "#8973")] - public override void Query_for_base_type_loads_all_owned_navs() - { - base.Query_for_base_type_loads_all_owned_navs(); - } - - [Fact(Skip = "#8973")] - public override void Query_for_branch_type_loads_all_owned_navs() - { - base.Query_for_branch_type_loads_all_owned_navs(); - } - - [Fact(Skip = "#8973")] - public override void Query_when_group_by() - { - base.Query_when_group_by(); - } - - [Fact(Skip = "#8973")] - public override void Query_when_subquery() - { - base.Query_when_subquery(); - } - - [Fact(Skip = "#8973")] - public override void Query_for_leaf_type_loads_all_owned_navs() - { - base.Query_for_leaf_type_loads_all_owned_navs(); - } - public class OwnedQuerySqliteFixture : OwnedQueryFixtureBase { protected override ITestStoreFactory TestStoreFactory => SqliteTestStoreFactory.Instance;