diff --git a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs index 3cf6470d407..f7d5e601fff 100644 --- a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs +++ b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs @@ -921,6 +921,12 @@ public static string MappedFunctionNotFound([CanBeNull] object entityType, [CanB GetString("MappedFunctionNotFound", nameof(entityType), nameof(functionName)), entityType, functionName); + /// + /// Unable to identify the concrete entity type to materialize in TPT hierarchy. + /// + public static string QueryUnableToIdentifyConcreteTypeInTPT + => GetString("QueryUnableToIdentifyConcreteTypeInTPT"); + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/EFCore.Relational/Properties/RelationalStrings.resx b/src/EFCore.Relational/Properties/RelationalStrings.resx index 59b0551fc94..87d70631191 100644 --- a/src/EFCore.Relational/Properties/RelationalStrings.resx +++ b/src/EFCore.Relational/Properties/RelationalStrings.resx @@ -679,4 +679,7 @@ The entity type '{entityType}' is mapped to the DbFunction named '{functionName}', but no DbFunction with that name was found in the model. Ensure that the entity type mapping is configured using the model name of a function in the model. + + Unable to identify the concrete entity type to materialize in TPT hierarchy. + \ No newline at end of file diff --git a/src/EFCore.Relational/Query/EntityProjectionExpression.cs b/src/EFCore.Relational/Query/EntityProjectionExpression.cs index cf957cd0a0d..242cda9f751 100644 --- a/src/EFCore.Relational/Query/EntityProjectionExpression.cs +++ b/src/EFCore.Relational/Query/EntityProjectionExpression.cs @@ -23,49 +23,49 @@ namespace Microsoft.EntityFrameworkCore.Query /// public class EntityProjectionExpression : Expression { - private readonly IDictionary _propertyExpressionsCache - = new Dictionary(); - - private readonly IDictionary _navigationExpressionsCache + private readonly IDictionary _propertyExpressionMap = new Dictionary(); + private readonly IDictionary _ownedNavigationMap = new Dictionary(); - private readonly TableExpressionBase _innerTable; - private readonly bool _nullable; - /// /// Creates a new instance of the class. /// /// The entity type to shape. /// The table from which entity columns are being projected out. /// A bool value indicating whether this entity instance can be null. + [Obsolete("Use the constructor which takes populated column expressions map.", error: true)] public EntityProjectionExpression([NotNull] IEntityType entityType, [NotNull] TableExpressionBase innerTable, bool nullable) { - Check.NotNull(entityType, nameof(entityType)); - Check.NotNull(innerTable, nameof(innerTable)); - - EntityType = entityType; - _innerTable = innerTable; - _nullable = nullable; + throw new NotSupportedException(); } /// /// Creates a new instance of the class. /// /// The entity type to shape. - /// A dictionary of column expressions corresponding to properties of the entity type. - public EntityProjectionExpression([NotNull] IEntityType entityType, [NotNull] IDictionary propertyExpressions) + /// A dictionary of column expressions corresponding to properties of the entity type. + /// A dictionary of to identify each entity type in hierarchy. + public EntityProjectionExpression( + [NotNull] IEntityType entityType, + [NotNull] IDictionary propertyExpressionMap, + [CanBeNull] IReadOnlyDictionary entityTypeIdentifyingExpressionMap = null) { Check.NotNull(entityType, nameof(entityType)); - Check.NotNull(propertyExpressions, nameof(propertyExpressions)); + Check.NotNull(propertyExpressionMap, nameof(propertyExpressionMap)); EntityType = entityType; - _propertyExpressionsCache = propertyExpressions; + _propertyExpressionMap = propertyExpressionMap; + EntityTypeIdentifyingExpressionMap = entityTypeIdentifyingExpressionMap; } /// /// The entity type being projected out. /// public virtual IEntityType EntityType { get; } + /// + /// Dictionary of entity type identifying expressions. + /// + public virtual IReadOnlyDictionary EntityTypeIdentifyingExpressionMap { get; } /// public sealed override ExpressionType NodeType => ExpressionType.Extension; /// @@ -76,27 +76,31 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) { Check.NotNull(visitor, nameof(visitor)); - if (_innerTable != null) + var changed = false; + var propertyExpressionMap = new Dictionary(); + foreach (var expression in _propertyExpressionMap) { - var table = (TableExpressionBase)visitor.Visit(_innerTable); + var newExpression = (ColumnExpression)visitor.Visit(expression.Value); + changed |= newExpression != expression.Value; - return table != _innerTable - ? new EntityProjectionExpression(EntityType, table, _nullable) - : this; + propertyExpressionMap[expression.Key] = newExpression; } - var changed = false; - var newCache = new Dictionary(); - foreach (var expression in _propertyExpressionsCache) + Dictionary entityTypeIdentifyingExpressionMap = null; + if (EntityTypeIdentifyingExpressionMap != null) { - var newExpression = (ColumnExpression)visitor.Visit(expression.Value); - changed |= newExpression != expression.Value; + entityTypeIdentifyingExpressionMap = new Dictionary(); + foreach (var expression in EntityTypeIdentifyingExpressionMap) + { + var newExpression = (SqlExpression)visitor.Visit(expression.Value); + changed |= newExpression != expression.Value; - newCache[expression.Key] = newExpression; + entityTypeIdentifyingExpressionMap[expression.Key] = newExpression; + } } return changed - ? new EntityProjectionExpression(EntityType, newCache) + ? new EntityProjectionExpression(EntityType, propertyExpressionMap, entityTypeIdentifyingExpressionMap) : this; } @@ -106,18 +110,14 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) /// A new entity projection expression which can project nullable entity. public virtual EntityProjectionExpression MakeNullable() { - if (_innerTable != null) + var propertyExpressionMap = new Dictionary(); + foreach (var expression in _propertyExpressionMap) { - return new EntityProjectionExpression(EntityType, _innerTable, nullable: true); + propertyExpressionMap[expression.Key] = expression.Value.MakeNullable(); } - var newCache = new Dictionary(); - foreach (var expression in _propertyExpressionsCache) - { - newCache[expression.Key] = expression.Value.MakeNullable(); - } - - return new EntityProjectionExpression(EntityType, newCache); + // We don't need to process EntityTypeIdentifyingExpressionMap because they are already nullable + return new EntityProjectionExpression(EntityType, propertyExpressionMap, EntityTypeIdentifyingExpressionMap); } /// @@ -129,23 +129,32 @@ public virtual EntityProjectionExpression UpdateEntityType([NotNull] IEntityType { Check.NotNull(derivedType, nameof(derivedType)); - if (_innerTable != null) - { - return new EntityProjectionExpression(derivedType, _innerTable, _nullable); - } - - var propertyExpressionCache = new Dictionary(); - foreach (var kvp in _propertyExpressionsCache) + var propertyExpressionMap = new Dictionary(); + foreach (var kvp in _propertyExpressionMap) { var property = kvp.Key; if (derivedType.IsAssignableFrom(property.DeclaringEntityType) || property.DeclaringEntityType.IsAssignableFrom(derivedType)) { - propertyExpressionCache[property] = kvp.Value; + propertyExpressionMap[property] = kvp.Value; + } + } + + Dictionary entityTypeIdentifyingExpressionMap = null; + if (EntityTypeIdentifyingExpressionMap != null) + { + entityTypeIdentifyingExpressionMap = new Dictionary(); + foreach (var kvp in EntityTypeIdentifyingExpressionMap) + { + var entityType = kvp.Key; + if (entityType.IsStrictlyDerivedFrom(derivedType)) + { + entityTypeIdentifyingExpressionMap[entityType] = kvp.Value; + } } } - return new EntityProjectionExpression(derivedType, propertyExpressionCache); + return new EntityProjectionExpression(derivedType, propertyExpressionMap, entityTypeIdentifyingExpressionMap); } /// @@ -168,13 +177,7 @@ public virtual ColumnExpression BindProperty([NotNull] IProperty property) property.Name)); } - if (!_propertyExpressionsCache.TryGetValue(property, out var expression)) - { - expression = new ColumnExpression(property, _innerTable, _nullable); - _propertyExpressionsCache[property] = expression; - } - - return expression; + return _propertyExpressionMap[property]; } /// @@ -198,7 +201,7 @@ public virtual void AddNavigationBinding([NotNull] INavigation navigation, [NotN navigation.Name)); } - _navigationExpressionsCache[navigation] = entityShaper; + _ownedNavigationMap[navigation] = entityShaper; } /// @@ -222,7 +225,7 @@ public virtual EntityShaperExpression BindNavigation([NotNull] INavigation navig navigation.Name)); } - return _navigationExpressionsCache.TryGetValue(navigation, out var expression) + return _ownedNavigationMap.TryGetValue(navigation, out var expression) ? expression : null; } diff --git a/src/EFCore.Relational/Query/RelationalEntityShaperExpression.cs b/src/EFCore.Relational/Query/RelationalEntityShaperExpression.cs index 974a67e2b13..5f47d60c19e 100644 --- a/src/EFCore.Relational/Query/RelationalEntityShaperExpression.cs +++ b/src/EFCore.Relational/Query/RelationalEntityShaperExpression.cs @@ -4,10 +4,13 @@ using System; using System.Linq; using System.Linq.Expressions; +using System.Reflection; using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Utilities; namespace Microsoft.EntityFrameworkCore.Query @@ -23,6 +26,14 @@ namespace Microsoft.EntityFrameworkCore.Query /// public class RelationalEntityShaperExpression : EntityShaperExpression { + private static readonly MethodInfo _createUnableToIdentifyConcreteTypeException + = typeof(RelationalEntityShaperExpression).GetTypeInfo() + .GetDeclaredMethod(nameof(CreateUnableToIdentifyConcreteTypeException)); + + [UsedImplicitly] + private static Exception CreateUnableToIdentifyConcreteTypeException() + => new InvalidOperationException(RelationalStrings.QueryUnableToIdentifyConcreteTypeInTPT); + /// /// Creates a new instance of the class. /// @@ -55,11 +66,35 @@ protected override LambdaExpression GenerateMaterializationCondition(IEntityType { Check.NotNull(entityType, nameof(EntityType)); - var baseCondition = base.GenerateMaterializationCondition(entityType, nullable); + LambdaExpression baseCondition; + if (entityType.GetDiscriminatorProperty() == null + && entityType.GetDirectlyDerivedTypes().Any()) + { + // TPT + var valueBufferParameter = Parameter(typeof(ValueBuffer)); + var body = entityType.IsAbstract() + ? Block(Throw(Call(_createUnableToIdentifyConcreteTypeException)), Constant(null, typeof(IEntityType))) + : (Expression)Constant(entityType, typeof(IEntityType)); + + var concreteEntityTypes = entityType.GetDerivedTypes().Where(dt => !dt.IsAbstract()).ToArray(); + for (var i = 0; i < concreteEntityTypes.Length; i++) + { + body = Condition( + valueBufferParameter.CreateValueBufferReadValueExpression(typeof(bool), i, property: null), + Constant(concreteEntityTypes[i], typeof(IEntityType)), + body); + } + + baseCondition = Lambda(body, valueBufferParameter); + } + else + { + baseCondition = base.GenerateMaterializationCondition(entityType, nullable); + } if (entityType.FindPrimaryKey() != null) { - var linkingFks = entityType.GetViewOrTableMappings().SingleOrDefault()?.Table.GetRowInternalForeignKeys(entityType); + var linkingFks = entityType.GetViewOrTableMappings().FirstOrDefault()?.Table.GetRowInternalForeignKeys(entityType); if (linkingFks != null && linkingFks.Any()) { diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index 24215d82b11..d95f79832ad 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -755,16 +755,47 @@ protected override ShapedQueryExpression TranslateOfType(ShapedQueryExpression s var derivedType = entityType.GetDerivedTypes().SingleOrDefault(et => et.ClrType == resultType); if (derivedType != null) { - if (!derivedType.GetRootType().GetIsDiscriminatorMappingComplete() - || !derivedType.GetAllBaseTypesInclusiveAscending() - .All(e => (e == derivedType || e.IsAbstract()) && !HasSiblings(e))) + var discriminatorProperty = entityType.GetDiscriminatorProperty(); + if (discriminatorProperty == null) { var selectExpression = (SelectExpression)source.QueryExpression; var concreteEntityTypes = derivedType.GetConcreteDerivedTypesInclusive().ToList(); var projectionBindingExpression = (ProjectionBindingExpression)entityShaperExpression.ValueBufferExpression; - var entityProjectionExpression = (EntityProjectionExpression)selectExpression.GetMappedProjection( - projectionBindingExpression.ProjectionMember); - var discriminatorColumn = entityProjectionExpression.BindProperty(entityType.GetDiscriminatorProperty()); + + var projectionMember = projectionBindingExpression.ProjectionMember; + Check.DebugAssert( + new ProjectionMember().Equals(projectionMember), + "Invalid ProjectionMember when processing OfType"); + + var entityProjectionExpression = (EntityProjectionExpression)selectExpression.GetMappedProjection(projectionMember); + + var predicate = entityProjectionExpression.EntityTypeIdentifyingExpressionMap + .Where(kvp => concreteEntityTypes.Contains(kvp.Key)) + .Select(kvp => kvp.Value) + .Aggregate((l, r) => _sqlExpressionFactory.OrElse(l, r)); + + selectExpression.ApplyPredicate(predicate); + selectExpression.ReplaceProjectionMapping( + new Dictionary + { + { projectionMember, entityProjectionExpression.UpdateEntityType(derivedType) } + }); + } + else if (!derivedType.GetRootType().GetIsDiscriminatorMappingComplete() + || !derivedType.GetAllBaseTypesInclusiveAscending() + .All(e => (e == derivedType || e.IsAbstract()) && !HasSiblings(e))) + { + var selectExpression = (SelectExpression)source.QueryExpression; + var concreteEntityTypes = derivedType.GetConcreteDerivedTypesInclusive().ToList(); + var projectionBindingExpression = (ProjectionBindingExpression)entityShaperExpression.ValueBufferExpression; + + var projectionMember = projectionBindingExpression.ProjectionMember; + Check.DebugAssert( + new ProjectionMember().Equals(projectionMember), + "Invalid ProjectionMember when processing OfType"); + + var entityProjectionExpression = (EntityProjectionExpression)selectExpression.GetMappedProjection(projectionMember); + var discriminatorColumn = entityProjectionExpression.BindProperty(discriminatorProperty); var predicate = concreteEntityTypes.Count == 1 ? _sqlExpressionFactory.Equal( @@ -776,19 +807,10 @@ protected override ShapedQueryExpression TranslateOfType(ShapedQueryExpression s negated: false); selectExpression.ApplyPredicate(predicate); - - var projectionMember = projectionBindingExpression.ProjectionMember; - - Check.DebugAssert( - new ProjectionMember().Equals(projectionMember), - "Invalid ProjectionMember when processing OfType"); - - var entityProjection = (EntityProjectionExpression)selectExpression.GetMappedProjection(projectionMember); - selectExpression.ReplaceProjectionMapping( new Dictionary { - { projectionMember, entityProjection.UpdateEntityType(derivedType) } + { projectionMember, entityProjectionExpression.UpdateEntityType(derivedType) } }); } @@ -1296,16 +1318,34 @@ private Expression TryExpand(Expression source, MemberIdentity member) var innerShaper = entityProjectionExpression.BindNavigation(navigation); if (innerShaper == null) { - if (entityType.GetViewOrTableMappings().Single().Table - .GetReferencingRowInternalForeignKeys(foreignKey.PrincipalEntityType)?.Contains(foreignKey) == true) + // Owned types don't support inheritance See https://github.com/dotnet/efcore/issues/9630 + // So there is no handling for dependent having TPT + + // If navigation is defined on derived type and entity type is part of TPT then we need to get ITableBase for derived type. + var table = navigation.DeclaringEntityType.BaseType == null + || entityType.GetDiscriminatorProperty() != null + ? navigation.DeclaringEntityType.GetViewOrTableMappings().Single().Table + : navigation.DeclaringEntityType.GetViewOrTableMappings().Select(tm => tm.Table) + .Except(navigation.DeclaringEntityType.BaseType.GetViewOrTableMappings().Select(tm => tm.Table)) + .Single(); + if (table.GetReferencingRowInternalForeignKeys(foreignKey.PrincipalEntityType)?.Contains(foreignKey) == true) { - // Since we are not going to update table or visit, we always generate propertyExpressions - // We just first column of PK to figure out the base table + // Mapped to same table + // We get identifying column to figure out tableExpression to pull columns from and nullability of most principal side var identifyingColumn = entityProjectionExpression.BindProperty(entityType.FindPrimaryKey().Properties.First()); + var principalNullable = identifyingColumn.IsNullable + // Also make nullable if navigation is on derived type and and principal is TPT + // Since identifying PK would be non-nullable but principal can still be null + // Derived owned navigation does not de-dupe the PK column which for principal is from base table + // and for dependent on derived table + || (entityType.GetDiscriminatorProperty() == null + && navigation.DeclaringEntityType.IsStrictlyDerivedFrom(entityShaperExpression.EntityType)); + var propertyExpressions = identifyingColumn.Table is TableExpression innerTable - ? GetPropertyExpressionsFromTable(targetEntityType, innerTable, identifyingColumn.IsNullable) - // Pull columns out of inner subquery - : GetPropertyExpressionsFromSubquery(targetEntityType, identifyingColumn, identifyingColumn.IsNullable); + ? GetPropertyExpressionsFromTable( + targetEntityType, table, _selectExpression, innerTable, principalNullable) + // If the principal table is SelectExpression then we may need to populate inner projection + : GetPropertyExpressionsFromSubquery(targetEntityType, table, identifyingColumn, principalNullable); innerShaper = new RelationalEntityShaperExpression( targetEntityType, new EntityProjectionExpression(targetEntityType, propertyExpressions), true); @@ -1334,10 +1374,10 @@ private Expression TryExpand(Expression source, MemberIdentity member) var joinPredicate = _sqlTranslator.Translate(Expression.Equal(outerKey, innerKey)); _selectExpression.AddLeftJoin(innerSelectExpression, joinPredicate); var leftJoinTable = ((LeftJoinExpression)_selectExpression.Tables.Last()).Table; + var propertyExpressions = GetPropertyExpressionsFromJoinedTable(targetEntityType, table, leftJoinTable); + innerShaper = new RelationalEntityShaperExpression( - targetEntityType, - new EntityProjectionExpression(targetEntityType, leftJoinTable, true), - true); + targetEntityType, new EntityProjectionExpression(targetEntityType, propertyExpressions), true); } entityProjectionExpression.AddNavigationBinding(navigation, innerShaper); @@ -1346,13 +1386,19 @@ private Expression TryExpand(Expression source, MemberIdentity member) return innerShaper; } + private static IDictionary GetPropertyExpressionsFromSubquery( + IEntityType entityType, ITableBase table, ColumnExpression identifyingColumn, bool nullable) + { + var subquery = (SelectExpression)identifyingColumn.Table; + var subqueryIdentifyingColumn = (ColumnExpression)subquery.Projection + .SingleOrDefault(e => string.Equals(e.Alias, identifyingColumn.Name, StringComparison.OrdinalIgnoreCase)).Expression; + var subqueryPropertyExpressions = subqueryIdentifyingColumn.Table is TableExpression innerTable + ? GetPropertyExpressionsFromTable(entityType, table, subquery, innerTable, nullable) + : GetPropertyExpressionsFromSubquery(entityType, table, subqueryIdentifyingColumn, nullable); - private static IDictionary LiftPropertyExpressionsFromSubquery( - IDictionary propertyExpressions, SelectExpression subquery) - { var newPropertyExpressions = new Dictionary(); - foreach (var item in propertyExpressions) + foreach (var item in subqueryPropertyExpressions) { newPropertyExpressions[item.Key] = new ColumnExpression( subquery.Projection[subquery.AddToProjection(item.Value)], subquery); @@ -1361,28 +1407,49 @@ private static IDictionary LiftPropertyExpressionsF return newPropertyExpressions; } - private static IDictionary GetPropertyExpressionsFromSubquery( - IEntityType entityType, ColumnExpression identifyingColumn, bool nullable) + private static IDictionary GetPropertyExpressionsFromTable( + IEntityType entityType, ITableBase table, SelectExpression selectExpression, TableExpression tableExpression, bool nullable) { - var subquery = (SelectExpression)identifyingColumn.Table; - var subqueryIdentifyingColumn = (ColumnExpression)subquery.Projection - .SingleOrDefault(e => string.Equals(e.Alias, identifyingColumn.Name, StringComparison.OrdinalIgnoreCase)).Expression; + if (!string.Equals(tableExpression.Name, table.Name, StringComparison.OrdinalIgnoreCase)) + { + // Fetch the table for the type which is defininig the navigation since dependent would be in that table + tableExpression = selectExpression.Tables + .Select(t => (t as InnerJoinExpression)?.Table ?? (t as LeftJoinExpression)?.Table ?? t) + .Cast() + .First(t => string.Equals(t.Name, table.Name, StringComparison.OrdinalIgnoreCase)); + } - var subqueryPropertyExpressions = subqueryIdentifyingColumn.Table is TableExpression innerTable - ? GetPropertyExpressionsFromTable(entityType, innerTable, nullable) - : GetPropertyExpressionsFromSubquery(entityType, subqueryIdentifyingColumn, nullable); + var propertyExpressions = new Dictionary(); + foreach (var property in entityType + .GetAllBaseTypes().Concat(entityType.GetDerivedTypesInclusive()).SelectMany(EntityTypeExtensions.GetDeclaredProperties)) + { + var column = table is ITable + ? (IColumnBase)property.GetTableColumnMappings().Where(cm => cm.TableMapping.Table == table + && cm.TableMapping.EntityType == property.DeclaringEntityType).Single().Column + : property.GetViewColumnMappings().Where(cm => cm.TableMapping.Table == table + && cm.TableMapping.EntityType == property.DeclaringEntityType).Single().Column; + + propertyExpressions[property] = new ColumnExpression( + property, column, tableExpression, nullable || !property.IsPrimaryKey()); + } - return LiftPropertyExpressionsFromSubquery(subqueryPropertyExpressions, subquery); + return propertyExpressions; } - private static IDictionary GetPropertyExpressionsFromTable( - IEntityType entityType, TableExpression table, bool nullable) + private static IDictionary GetPropertyExpressionsFromJoinedTable( + IEntityType entityType, ITableBase table, TableExpressionBase tableExpression) { var propertyExpressions = new Dictionary(); foreach (var property in entityType .GetAllBaseTypes().Concat(entityType.GetDerivedTypesInclusive()).SelectMany(EntityTypeExtensions.GetDeclaredProperties)) { - propertyExpressions[property] = new ColumnExpression(property, table, nullable || !property.IsPrimaryKey()); + var column = table is ITable + ? (IColumnBase)property.GetTableColumnMappings().Where(cm => cm.TableMapping.Table == table + && cm.TableMapping.EntityType == property.DeclaringEntityType).Single().Column + : property.GetViewColumnMappings().Where(cm => cm.TableMapping.Table == table + && cm.TableMapping.EntityType == property.DeclaringEntityType).Single().Column; + + propertyExpressions[property] = new ColumnExpression(property, column, tableExpression, nullable: true); } return propertyExpressions; diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs index 00921b816cd..40e97ee6a14 100644 --- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs @@ -106,6 +106,8 @@ private static readonly MethodInfo _collectionAccessorAddMethodInfo // States to convert code to data reader read private readonly IDictionary> _materializationContextBindings = new Dictionary>(); + private readonly IDictionary entityTypeIdentifyingExpressionOffsets + = new Dictionary(); public ShaperProcessingExpressionVisitor( RelationalShapedQueryCompilingExpressionVisitor parentVisitor, @@ -343,8 +345,9 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression) var newExpression = (NewExpression)binaryExpression.Right; var projectionBindingExpression = (ProjectionBindingExpression)newExpression.Arguments[0]; - _materializationContextBindings[parameterExpression] - = (IDictionary)GetProjectionIndex(projectionBindingExpression); + var propertyMap = (IDictionary)GetProjectionIndex(projectionBindingExpression); + _materializationContextBindings[parameterExpression] = propertyMap; + entityTypeIdentifyingExpressionOffsets[parameterExpression] = propertyMap.Values.Max() + 1; var updatedExpression = Expression.New( newExpression.Constructor, @@ -371,14 +374,15 @@ protected override Expression VisitExtension(Expression extensionExpression) switch (extensionExpression) { - case EntityShaperExpression entityShaperExpression: + case RelationalEntityShaperExpression entityShaperExpression: { var key = GenerateKey((ProjectionBindingExpression)entityShaperExpression.ValueBufferExpression); if (!_variableShaperMapping.TryGetValue(key, out var accessor)) { var entityParameter = Expression.Parameter(entityShaperExpression.Type); _variables.Add(entityParameter); - var entityMaterializationExpression = Visit(_parentVisitor.InjectEntityMaterializers(entityShaperExpression)); + var entityMaterializationExpression = _parentVisitor.InjectEntityMaterializers(entityShaperExpression); + entityMaterializationExpression = Visit(entityMaterializationExpression); _expressions.Add(Expression.Assign(entityParameter, entityMaterializationExpression)); @@ -818,19 +822,17 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp && methodCallExpression.Method.GetGenericMethodDefinition() == Infrastructure.ExpressionExtensions.ValueBufferTryReadValueMethod) { var property = (IProperty)((ConstantExpression)methodCallExpression.Arguments[2]).Value; - var propertyProjectionMap = methodCallExpression.Arguments[0] is ProjectionBindingExpression projectionBindingExpression - ? (IDictionary)GetProjectionIndex(projectionBindingExpression) - : _materializationContextBindings[ - (ParameterExpression)((MethodCallExpression)methodCallExpression.Arguments[0]).Object]; - - var projectionIndex = propertyProjectionMap[property]; + var mappingParameter = (ParameterExpression)((MethodCallExpression)methodCallExpression.Arguments[0]).Object; + var projectionIndex = property == null + ? entityTypeIdentifyingExpressionOffsets[mappingParameter] + (int)((ConstantExpression)methodCallExpression.Arguments[1]).Value + : _materializationContextBindings[mappingParameter][property]; var projection = _selectExpression.Projection[projectionIndex]; return CreateGetValueExpression( _dataReaderParameter, projectionIndex, IsNullableProjection(projection), - property.GetRelationalTypeMapping(), + projection.Expression.TypeMapping, methodCallExpression.Type, property); } diff --git a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs index 7b82675d12e..3799d0c3f9f 100644 --- a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs @@ -747,19 +747,60 @@ protected override Expression VisitTypeBinary(TypeBinaryExpression typeBinaryExp if (derivedType != null) { var concreteEntityTypes = derivedType.GetConcreteDerivedTypesInclusive().ToList(); - var discriminatorColumn = BindProperty(entityReferenceExpression, entityType.GetDiscriminatorProperty()); - - return concreteEntityTypes.Count == 1 - ? _sqlExpressionFactory.Equal( - discriminatorColumn, - _sqlExpressionFactory.Constant(concreteEntityTypes[0].GetDiscriminatorValue())) - : (Expression)_sqlExpressionFactory.In( - discriminatorColumn, - _sqlExpressionFactory.Constant(concreteEntityTypes.Select(et => et.GetDiscriminatorValue()).ToList()), - negated: false); - } + var discriminatorProperty = entityType.GetDiscriminatorProperty(); + if (discriminatorProperty == null) + { + // TPT + if (entityReferenceExpression.SubqueryEntity != null) + { + var entityShaper = (EntityShaperExpression)entityReferenceExpression.SubqueryEntity.ShaperExpression; + var entityProjection = (EntityProjectionExpression)Visit(entityShaper.ValueBufferExpression); + var subSelectExpression = (SelectExpression)entityReferenceExpression.SubqueryEntity.QueryExpression; + + var predicate = entityProjection.EntityTypeIdentifyingExpressionMap + .Where(kvp => concreteEntityTypes.Contains(kvp.Key)) + .Select(kvp => kvp.Value) + .Aggregate((l, r) => _sqlExpressionFactory.OrElse(l, r)); + + subSelectExpression.ApplyPredicate(predicate); + subSelectExpression.ReplaceProjectionMapping(new Dictionary()); + if (subSelectExpression.Limit == null + && subSelectExpression.Offset == null) + { + subSelectExpression.ClearOrdering(); + } + + return _sqlExpressionFactory.Exists(subSelectExpression, false); + } + + if (entityReferenceExpression.ParameterEntity != null) + { + var entityProjection = (EntityProjectionExpression)Visit( + entityReferenceExpression.ParameterEntity.ValueBufferExpression); + + return entityProjection.EntityTypeIdentifyingExpressionMap + .Where(kvp => concreteEntityTypes.Contains(kvp.Key)) + .Select(kvp => kvp.Value) + .Aggregate((l, r) => _sqlExpressionFactory.OrElse(l, r)); + } + } + else + { + var discriminatorColumn = BindProperty(entityReferenceExpression, discriminatorProperty); - return _sqlExpressionFactory.Constant(false); + if (discriminatorColumn != null) + { + return concreteEntityTypes.Count == 1 + ? _sqlExpressionFactory.Equal( + discriminatorColumn, + _sqlExpressionFactory.Constant(concreteEntityTypes[0].GetDiscriminatorValue())) + : (Expression)_sqlExpressionFactory.In( + discriminatorColumn, + _sqlExpressionFactory.Constant(concreteEntityTypes.Select(et => et.GetDiscriminatorValue()).ToList()), + negated: false); + } + } + } } return null; diff --git a/src/EFCore.Relational/Query/SqlExpressionFactory.cs b/src/EFCore.Relational/Query/SqlExpressionFactory.cs index 0a2aaa7875e..23adc434eaf 100644 --- a/src/EFCore.Relational/Query/SqlExpressionFactory.cs +++ b/src/EFCore.Relational/Query/SqlExpressionFactory.cs @@ -757,7 +757,7 @@ public virtual SelectExpression Select(IEntityType entityType) { Check.NotNull(entityType, nameof(entityType)); - var selectExpression = new SelectExpression(entityType); + var selectExpression = new SelectExpression(entityType, this); AddConditions(selectExpression, entityType); return selectExpression; @@ -801,7 +801,7 @@ private void AddSelfConditions(SelectExpression selectExpression, IEntityType en } // Add conditions if dependent sharing table with principal - table ??= entityType.GetViewOrTableMappings().SingleOrDefault()?.Table; + table ??= entityType.GetViewOrTableMappings().FirstOrDefault()?.Table; if (table != null && table.GetRowInternalForeignKeys(entityType).Any() && !discriminatorAdded) @@ -814,7 +814,7 @@ private void AddConditions(SelectExpression selectExpression, IEntityType entity { AddSelfConditions(selectExpression, entityType, table); // Add inner join to principal if table sharing - table ??= entityType.GetViewOrTableMappings().SingleOrDefault()?.Table; + table ??= entityType.GetViewOrTableMappings().FirstOrDefault()?.Table; if (table != null) { var linkingFks = table.GetRowInternalForeignKeys(entityType); @@ -828,7 +828,7 @@ private void AddConditions(SelectExpression selectExpression, IEntityType entity } else { - var dependentSelectExpression = new SelectExpression(entityType); + var dependentSelectExpression = new SelectExpression(entityType, this); AddSelfConditions(dependentSelectExpression, entityType, table); AddInnerJoin(dependentSelectExpression, foreignKey, table); selectExpression.ApplyUnion(dependentSelectExpression, distinct: true); @@ -843,8 +843,8 @@ private void AddInnerJoin(SelectExpression selectExpression, IForeignKey foreign var outerIsPrincipal = foreignKey.PrincipalEntityType.IsAssignableFrom(outerEntityProjection.EntityType); var innerSelect = outerIsPrincipal - ? new SelectExpression(foreignKey.DeclaringEntityType) - : new SelectExpression(foreignKey.PrincipalEntityType); + ? new SelectExpression(foreignKey.DeclaringEntityType, this) + : new SelectExpression(foreignKey.PrincipalEntityType, this); if (outerIsPrincipal) { @@ -944,7 +944,7 @@ private void AddOptionalDependentConditions( // other dependents. foreach (var referencingFk in entityType.GetReferencingForeignKeys()) { - var otherSelectExpression = new SelectExpression(entityType); + var otherSelectExpression = new SelectExpression(entityType, this); var sameTable = table.GetRowInternalForeignKeys(referencingFk.DeclaringEntityType).Any(); AddInnerJoin( diff --git a/src/EFCore.Relational/Query/SqlExpressions/ColumnExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/ColumnExpression.cs index 902ee50976e..0feada9e848 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/ColumnExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/ColumnExpression.cs @@ -24,18 +24,13 @@ namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions // Class is sealed because there are no public/protected constructors. Can be unsealed if this is changed. public sealed class ColumnExpression : SqlExpression { - internal ColumnExpression(IProperty property, TableExpressionBase table, bool nullable) + internal ColumnExpression(IProperty property, IColumnBase column, TableExpressionBase table, bool nullable) : this( - property.GetTableColumnMappings().Cast().Concat(property.GetViewColumnMappings()) - .FirstOrDefault()?.Column.Name // TODO: this should take table into account - ?? property.GetColumnName(), + column?.Name ?? property.GetColumnName(), table, property.ClrType, property.GetRelationalTypeMapping(), - nullable - || (property.GetTableColumnMappings().Cast().Concat(property.GetViewColumnMappings()) - .FirstOrDefault()?.Column.IsNullable // TODO: this should take table into account - ?? property.IsColumnNullable())) + nullable || (column?.IsNullable ?? property.IsColumnNullable())) { } diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index 3853994b059..66e63739b06 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -118,9 +118,116 @@ internal SelectExpression(SqlExpression projection) } } - internal SelectExpression(IEntityType entityType) - : this(entityType, new TableExpression(entityType.GetViewOrTableMappings().Single().Table)) + internal SelectExpression(IEntityType entityType, ISqlExpressionFactory sqlExpressionFactory) + : base(null) { + if ((entityType.BaseType == null && !entityType.GetDirectlyDerivedTypes().Any()) + || entityType.GetDiscriminatorProperty() != null) + { + // Key-less entities or TPH + var table = entityType.GetViewOrTableMappings().Single().Table; + var tableExpression = new TableExpression(table); + _tables.Add(tableExpression); + + var propertyExpressions = new Dictionary(); + foreach (var property in GetAllPropertiesInHierarchy(entityType)) + { + propertyExpressions[property] = GetColumn( + property, property.DeclaringEntityType, table, tableExpression, nullable: false); + } + + var entityProjection = new EntityProjectionExpression(entityType, propertyExpressions); + _projectionMapping[new ProjectionMember()] = entityProjection; + + if (entityType.FindPrimaryKey() != null) + { + foreach (var property in entityType.FindPrimaryKey().Properties) + { + _identifier.Add((propertyExpressions[property], property.GetKeyValueComparer())); + } + } + } + else + { + // TPT + var keyProperties = entityType.FindPrimaryKey().Properties; + List joinColumns = null; + var tables = new List(); + var columns = new Dictionary(); + foreach (var baseType in entityType.GetAllBaseTypesInclusive()) + { + var table = baseType.GetViewOrTableMappings().Single(m => !tables.Contains(m.Table)).Table; + tables.Add(table); + var tableExpression = new TableExpression(table); + foreach (var property in baseType.GetDeclaredProperties()) + { + columns[property] = GetColumn(property, baseType, table, tableExpression, nullable: false); + } + + if (_tables.Count == 0) + { + _tables.Add(tableExpression); + joinColumns = new List(); + foreach (var property in keyProperties) + { + var columnExpression = columns[property]; + joinColumns.Add(columnExpression); + _identifier.Add((columnExpression, property.GetKeyValueComparer())); + } + } + else + { + var innerColumns = keyProperties.Select(p => GetColumn(p, baseType, table, tableExpression, nullable: false)); + + var joinPredicate = joinColumns.Zip(innerColumns, (l, r) => sqlExpressionFactory.Equal(l, r)) + .Aggregate((l, r) => sqlExpressionFactory.AndAlso(l, r)); + + var joinExpression = new InnerJoinExpression(tableExpression, joinPredicate); + _tables.Add(joinExpression); + } + } + + var discriminatorExpressions = new Dictionary(); + + foreach (var derivedType in entityType.GetDerivedTypes()) + { + var table = derivedType.GetViewOrTableMappings().Single(m => !tables.Contains(m.Table)).Table; + tables.Add(table); + var tableExpression = new TableExpression(table); + foreach (var property in derivedType.GetDeclaredProperties()) + { + columns[property] = GetColumn(property, derivedType, table, tableExpression, nullable: true); + } + + var keyColumns = keyProperties.Select(p => GetColumn(p, derivedType, table, tableExpression, nullable: true)).ToArray(); + + if (!derivedType.IsAbstract()) + { + discriminatorExpressions[derivedType] = sqlExpressionFactory.IsNotNull(keyColumns[0]); + } + + var joinPredicate = joinColumns.Zip(keyColumns, (l, r) => sqlExpressionFactory.Equal(l, r)) + .Aggregate((l, r) => sqlExpressionFactory.AndAlso(l, r)); + + var joinExpression = new LeftJoinExpression(tableExpression, joinPredicate); + _tables.Add(joinExpression); + } + + var entityProjection = new EntityProjectionExpression(entityType, columns, discriminatorExpressions); + _projectionMapping[new ProjectionMember()] = entityProjection; + } + + static ColumnExpression GetColumn( + IProperty property, IEntityType currentEntityType, ITableBase table, TableExpression tableExpression, bool nullable) + { + var column = table is ITable + ? (IColumnBase)property.GetTableColumnMappings().Single(cm => cm.TableMapping.Table == table + && cm.TableMapping.EntityType == currentEntityType).Column + : property.GetViewColumnMappings().Single(cm => cm.TableMapping.Table == table + && cm.TableMapping.EntityType == currentEntityType).Column; + + return new ColumnExpression(property, column, tableExpression, nullable: nullable); + } } internal SelectExpression(IEntityType entityType, TableExpressionBase tableExpression) @@ -128,7 +235,13 @@ internal SelectExpression(IEntityType entityType, TableExpressionBase tableExpre { _tables.Add(tableExpression); - var entityProjection = new EntityProjectionExpression(entityType, tableExpression, false); + var propertyExpressions = new Dictionary(); + foreach (var property in GetAllPropertiesInHierarchy(entityType)) + { + propertyExpressions[property] = new ColumnExpression(property, null, tableExpression, nullable: false); + } + + var entityProjection = new EntityProjectionExpression(entityType, propertyExpressions); _projectionMapping[new ProjectionMember()] = entityProjection; if (entityType.FindPrimaryKey() != null) @@ -176,12 +289,19 @@ public void ApplyProjection() if (keyValuePair.Value is EntityProjectionExpression entityProjection) { var map = new Dictionary(); - foreach (var property in GetAllPropertiesInHierarchy(entityProjection.EntityType)) { map[property] = AddToProjection(entityProjection.BindProperty(property)); } + if (entityProjection.EntityTypeIdentifyingExpressionMap != null) + { + foreach (var kvp in entityProjection.EntityTypeIdentifyingExpressionMap) + { + AddToProjection(kvp.Value, $"Is{kvp.Key.ShortName()}"); + } + } + result[keyValuePair.Key] = Constant(map); } else @@ -254,19 +374,23 @@ private int AddToProjection(SqlExpression sqlExpression, string alias) return existingIndex; } - var baseAlias = alias ?? (sqlExpression as ColumnExpression)?.Name ?? (Alias != null ? "c" : null); - var currentAlias = baseAlias ?? ""; - if (Alias != null - && baseAlias != null) + var baseAlias = !string.IsNullOrEmpty(alias) + ? alias + : (sqlExpression as ColumnExpression)?.Name ?? (Alias != null ? "c" : null); + if (Alias != null) { var counter = 0; + Check.DebugAssert(baseAlias != null, "baseAlias should be non-null since this is a subquery."); + + var currentAlias = baseAlias; while (_projection.Any(pe => string.Equals(pe.Alias, currentAlias, StringComparison.OrdinalIgnoreCase))) { currentAlias = $"{baseAlias}{counter++}"; } + baseAlias = currentAlias; } - _projection.Add(new ProjectionExpression(sqlExpression, currentAlias)); + _projection.Add(new ProjectionExpression(sqlExpression, baseAlias ?? "")); return _projection.Count - 1; } @@ -288,6 +412,14 @@ public IDictionary AddToProjection([NotNull] EntityProjectionExp dictionary[property] = AddToProjection(entityProjection.BindProperty(property)); } + if (entityProjection.EntityTypeIdentifyingExpressionMap != null) + { + foreach (var kvp in entityProjection.EntityTypeIdentifyingExpressionMap) + { + AddToProjection(kvp.Value, $"Is{kvp.Key.ShortName()}"); + } + } + _entityProjectionCache[entityProjection] = dictionary; } @@ -704,7 +836,7 @@ private void ApplySetOperation(SetOperationType setOperationType, SelectExpressi if (joinedMapping.Value1 is EntityProjectionExpression entityProjection1 && joinedMapping.Value2 is EntityProjectionExpression entityProjection2) { - HandleEntityMapping(joinedMapping.Key, select1, entityProjection1, select2, entityProjection2); + HandleEntityProjection(joinedMapping.Key, select1, entityProjection1, select2, entityProjection2); continue; } @@ -752,7 +884,7 @@ private void ApplySetOperation(SetOperationType setOperationType, SelectExpressi _tables.Clear(); _tables.Add(setExpression); - void HandleEntityMapping( + void HandleEntityProjection( ProjectionMember projectionMember, SelectExpression select1, EntityProjectionExpression projection1, SelectExpression select2, EntityProjectionExpression projection2) @@ -765,15 +897,43 @@ void HandleEntityMapping( var propertyExpressions = new Dictionary(); foreach (var property in GetAllPropertiesInHierarchy(projection1.EntityType)) { - propertyExpressions[property] = AddSetOperationColumnProjections( + propertyExpressions[property] = GenerateColumnProjection( select1, projection1.BindProperty(property), select2, projection2.BindProperty(property)); } - _projectionMapping[projectionMember] = new EntityProjectionExpression(projection1.EntityType, propertyExpressions); + Dictionary entityTypeIdentifyingExpressionMap = null; + if (projection1.EntityTypeIdentifyingExpressionMap != null) + { + entityTypeIdentifyingExpressionMap = new Dictionary(); + // Both types will have same key since their entity types are same. + foreach (var kvp in projection1.EntityTypeIdentifyingExpressionMap) + { + var entityType = kvp.Key; + entityTypeIdentifyingExpressionMap[entityType] = GenerateEntityTypeIdentifyingExpression( + select1, kvp.Value, select2, projection2.EntityTypeIdentifyingExpressionMap[entityType], + $"Is{entityType.ShortName()}"); + } + } + + _projectionMapping[projectionMember] = new EntityProjectionExpression( + projection1.EntityType, propertyExpressions, entityTypeIdentifyingExpressionMap); } - ColumnExpression AddSetOperationColumnProjections( + ColumnExpression GenerateEntityTypeIdentifyingExpression( + SelectExpression select1, SqlExpression expression1, + SelectExpression select2, SqlExpression expression2, + string alias) + { + var innerProjection1 = new ProjectionExpression(expression1, alias); + var innerProjection2 = new ProjectionExpression(expression2, alias); + select1._projection.Add(innerProjection1); + select2._projection.Add(innerProjection2); + + return new ColumnExpression(innerProjection1, setExpression); + } + + ColumnExpression GenerateColumnProjection( SelectExpression select1, ColumnExpression column1, SelectExpression select2, ColumnExpression column2) { @@ -846,13 +1006,13 @@ public IDictionary PushdownIntoSubquery() // Projections may be present if added by lifting SingleResult/Enumerable in projection through join if (_projection.Any()) { - var projections = _projection.Select(pe => pe.Expression).ToList(); + var projections = _projection.ToList(); _projection.Clear(); foreach (var projection in projections) { - var outerColumn = subquery.GenerateOuterColumn(projection); + var outerColumn = subquery.GenerateOuterColumn(projection.Expression, projection.Alias); AddToProjection(outerColumn); - projectionMap[projection] = outerColumn; + projectionMap[projection.Expression] = outerColumn; } } @@ -960,7 +1120,23 @@ EntityProjectionExpression LiftEntityProjectionFromSubquery(EntityProjectionExpr propertyExpressions[property] = outerColumn; } - var newEntityProjection = new EntityProjectionExpression(entityProjection.EntityType, propertyExpressions); + Dictionary entityTypeIdentifyingExpressionMap = null; + if (entityProjection.EntityTypeIdentifyingExpressionMap != null) + { + entityTypeIdentifyingExpressionMap = new Dictionary(); + foreach (var entityTypeIdentifyingExpression in entityProjection.EntityTypeIdentifyingExpressionMap) + { + var innerProjection = entityTypeIdentifyingExpression.Value; + var outerColumn = subquery.GenerateOuterColumn( + innerProjection, $"Is{entityTypeIdentifyingExpression.Key.ShortName()}"); + projectionMap[innerProjection] = outerColumn; + entityTypeIdentifyingExpressionMap[entityTypeIdentifyingExpression.Key] = outerColumn; + } + } + + var newEntityProjection = new EntityProjectionExpression( + entityProjection.EntityType, propertyExpressions, entityTypeIdentifyingExpressionMap); + // Also lift nested entity projections foreach (var navigation in entityProjection.EntityType .GetAllBaseTypes().Concat(entityProjection.EntityType.GetDerivedTypesInclusive()) diff --git a/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs b/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs index ffe97d4bacc..386b256e7bc 100644 --- a/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs +++ b/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs @@ -501,12 +501,6 @@ private Expression MaterializeEntity( entityShaperExpression.MaterializationCondition.Body))); var concreteEntityTypes = entityType.GetConcreteDerivedTypesInclusive().ToArray(); - var discriminatorProperty = entityType.GetDiscriminatorProperty(); - if (discriminatorProperty == null - && concreteEntityTypes.Length > 1) - { - concreteEntityTypes = new [] { entityType }; - } var switchCases = new SwitchCase[concreteEntityTypes.Length]; for (var i = 0; i < concreteEntityTypes.Length; i++) diff --git a/test/EFCore.Relational.Specification.Tests/Query/TPTInheritanceQueryFixture.cs b/test/EFCore.Relational.Specification.Tests/Query/TPTInheritanceQueryFixture.cs index 5a4a8ad2f97..0c7b5fc3159 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/TPTInheritanceQueryFixture.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/TPTInheritanceQueryFixture.cs @@ -43,11 +43,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con modelBuilder.Entity().Property(e => e.SugarGrams).HasColumnName("SugarGrams"); modelBuilder.Entity().Property(e => e.CaffeineGrams).HasColumnName("CaffeineGrams"); - // Keyless entities are mapped to TPH - modelBuilder.Entity().HasNoKey().ToQuery( - () => context.Set().FromSqlRaw("SELECT * FROM Animals")); - modelBuilder.Entity().HasDiscriminator().HasValue("Kiwi"); - modelBuilder.Entity().HasDiscriminator().HasValue("Eagle"); + // Keyless entities are mapped to TPH so ignoring them + modelBuilder.Ignore(); + modelBuilder.Ignore(); + modelBuilder.Ignore(); + modelBuilder.Ignore(); } } } diff --git a/test/EFCore.Relational.Specification.Tests/Query/TPTInheritanceQueryTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/TPTInheritanceQueryTestBase.cs index 9941e982c91..4992fca6b21 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/TPTInheritanceQueryTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/TPTInheritanceQueryTestBase.cs @@ -16,232 +16,19 @@ public TPTInheritanceQueryTestBase(TFixture fixture) { } - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Byte_enum_value_constant_used_in_projection(bool async) - { - return base.Byte_enum_value_constant_used_in_projection(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_filter_all_animals(bool async) - { - return base.Can_filter_all_animals(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_include_animals(bool async) - { - return base.Can_include_animals(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_include_prey(bool async) - { - return base.Can_include_prey(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override void Can_insert_update_delete() - { - base.Can_insert_update_delete(); - } + // Keyless entities does not have TPT + public override Task Can_query_all_animal_views(bool async) => Task.CompletedTask; - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_query_all_animals(bool async) - { - return base.Can_query_all_animals(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_query_all_animal_views(bool async) - { - return base.Can_query_all_animal_views(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_query_all_birds(bool async) - { - return base.Can_query_all_birds(async); - } + // TPT does not have discriminator + public override Task Discriminator_used_when_projection_over_derived_type(bool async) => Task.CompletedTask; - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_query_all_plants(bool async) - { - return base.Can_query_all_plants(async); - } + // TPT does not have discriminator + public override Task Discriminator_used_when_projection_over_derived_type2(bool async) => Task.CompletedTask; - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_query_all_types_when_shared_column(bool async) - { - return base.Can_query_all_types_when_shared_column(async); - } + // TPT does not have discriminator + public override Task Discriminator_used_when_projection_over_of_type(bool async) => Task.CompletedTask; - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_query_just_kiwis(bool async) - { - return base.Can_query_just_kiwis(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_query_just_roses(bool async) - { - return base.Can_query_just_roses(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_query_when_shared_column(bool async) - { - return base.Can_query_when_shared_column(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_use_backwards_is_animal(bool async) - { - return base.Can_use_backwards_is_animal(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_use_backwards_of_type_animal(bool async) - { - return base.Can_use_backwards_of_type_animal(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_use_is_kiwi(bool async) - { - return base.Can_use_is_kiwi(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_use_is_kiwi_in_projection(bool async) - { - return base.Can_use_is_kiwi_in_projection(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_use_is_kiwi_with_other_predicate(bool async) - { - return base.Can_use_is_kiwi_with_other_predicate(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_use_of_type_animal(bool async) - { - return base.Can_use_of_type_animal(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_use_of_type_bird(bool async) - { - return base.Can_use_of_type_bird(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_use_of_type_bird_first(bool async) - { - return base.Can_use_of_type_bird_first(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_use_of_type_bird_predicate(bool async) - { - return base.Can_use_of_type_bird_predicate(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_use_of_type_bird_with_projection(bool async) - { - return base.Can_use_of_type_bird_with_projection(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_use_of_type_kiwi(bool async) - { - return base.Can_use_of_type_kiwi(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_use_of_type_kiwi_where_north_on_derived_property(bool async) - { - return base.Can_use_of_type_kiwi_where_north_on_derived_property(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_use_of_type_kiwi_where_south_on_derived_property(bool async) - { - return base.Can_use_of_type_kiwi_where_south_on_derived_property(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Can_use_of_type_rose(bool async) - { - return base.Can_use_of_type_rose(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Discriminator_used_when_projection_over_derived_type(bool async) - { - return base.Discriminator_used_when_projection_over_derived_type(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Discriminator_used_when_projection_over_derived_type2(bool async) - { - return base.Discriminator_used_when_projection_over_derived_type2(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Discriminator_used_when_projection_over_of_type(bool async) - { - return base.Discriminator_used_when_projection_over_of_type(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Discriminator_with_cast_in_shadow_property(bool async) - { - return base.Discriminator_with_cast_in_shadow_property(async); - } - - [ConditionalFact(Skip = "Issue#2266")] - public override void Member_access_on_intermediate_type_works() - { - base.Member_access_on_intermediate_type_works(); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task OfType_Union_OfType(bool async) - { - return base.OfType_Union_OfType(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task OfType_Union_subquery(bool async) - { - return base.OfType_Union_subquery(async); - } - - [ConditionalFact(Skip = "Issue#2266")] - public override void Setting_foreign_key_to_a_different_type_throws() - { - base.Setting_foreign_key_to_a_different_type_throws(); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Subquery_OfType(bool async) - { - return base.Subquery_OfType(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Union_entity_equality(bool async) - { - return base.Union_entity_equality(async); - } - - [ConditionalTheory(Skip = "Issue#2266")] - public override Task Union_siblings_with_duplicate_property_in_subquery(bool async) - { - return base.Union_siblings_with_duplicate_property_in_subquery(async); - } + // TPT does not have discriminator + public override Task Discriminator_with_cast_in_shadow_property(bool async) => Task.CompletedTask; } } diff --git a/test/EFCore.Relational.Specification.Tests/Query/TPTRelationshipsQueryTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/TPTRelationshipsQueryTestBase.cs index b8b2fefa817..92132f56b9f 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/TPTRelationshipsQueryTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/TPTRelationshipsQueryTestBase.cs @@ -13,230 +13,5 @@ protected TPTRelationshipsQueryTestBase(TFixture fixture) : base(fixture) { } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Changes_in_derived_related_entities_are_detected() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_reference_with_inheritance() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_reference_with_inheritance_reverse() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_self_reference_with_inheritance() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_self_reference_with_inheritance_reverse() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_reference_with_inheritance_with_filter() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_reference_with_inheritance_with_filter_reverse() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_reference_without_inheritance() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_reference_without_inheritance_reverse() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_reference_without_inheritance_with_filter() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_reference_without_inheritance_with_filter_reverse() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_collection_with_inheritance() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_collection_with_inheritance_reverse() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_collection_with_inheritance_with_filter() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_collection_with_inheritance_with_filter_reverse() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_collection_without_inheritance() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_collection_without_inheritance_reverse() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_collection_without_inheritance_with_filter() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_collection_without_inheritance_with_filter_reverse() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_reference_with_inheritance_on_derived1() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_reference_with_inheritance_on_derived2() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_reference_with_inheritance_on_derived4() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_reference_with_inheritance_on_derived_reverse() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_reference_with_inheritance_on_derived_with_filter1() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_reference_with_inheritance_on_derived_with_filter2() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_reference_with_inheritance_on_derived_with_filter4() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_reference_with_inheritance_on_derived_with_filter_reverse() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_reference_without_inheritance_on_derived1() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_reference_without_inheritance_on_derived2() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_reference_without_inheritance_on_derived_reverse() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_collection_with_inheritance_on_derived1() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_collection_with_inheritance_on_derived2() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_collection_with_inheritance_on_derived3() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Include_collection_with_inheritance_on_derived_reverse() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Nested_include_with_inheritance_reference_reference() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Nested_include_with_inheritance_reference_reference_on_base() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Nested_include_with_inheritance_reference_reference_reverse() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Nested_include_with_inheritance_reference_collection() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Nested_include_with_inheritance_reference_collection_on_base() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Nested_include_with_inheritance_reference_collection_reverse() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Nested_include_with_inheritance_collection_reference() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Nested_include_with_inheritance_collection_reference_reverse() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Nested_include_with_inheritance_collection_collection() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Nested_include_with_inheritance_collection_collection_reverse() - { - } - - [ConditionalFact(Skip = "Issue #2266")] - public override void Nested_include_collection_reference_on_non_entity_base() - { - } } } diff --git a/test/EFCore.Relational.Specification.Tests/TPTTableSplittingTestBase.cs b/test/EFCore.Relational.Specification.Tests/TPTTableSplittingTestBase.cs new file mode 100644 index 00000000000..a631a00e852 --- /dev/null +++ b/test/EFCore.Relational.Specification.Tests/TPTTableSplittingTestBase.cs @@ -0,0 +1,554 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.TestModels.TransportationModel; +using Microsoft.EntityFrameworkCore.TestUtilities; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Xunit; +using Xunit.Abstractions; + +// ReSharper disable InconsistentNaming +namespace Microsoft.EntityFrameworkCore +{ + public abstract class TPTTableSplittingTestBase + { + protected TPTTableSplittingTestBase(ITestOutputHelper testOutputHelper) + { + TestSqlLoggerFactory = (TestSqlLoggerFactory)TestStoreFactory.CreateListLoggerFactory(_ => true); + //TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + [ConditionalFact] + public virtual void Can_update_just_dependents() + { + using (CreateTestStore(OnModelCreating)) + { + Operator firstOperator; + Engine firstEngine; + using (var context = CreateContext()) + { + firstOperator = context.Set().OrderBy(o => o.VehicleName).First(); + firstOperator.Name += "1"; + firstEngine = context.Set().OrderBy(o => o.VehicleName).First(); + firstEngine.Description += "1"; + + context.SaveChanges(); + + Assert.Empty(context.ChangeTracker.Entries().Where(e => e.State != EntityState.Unchanged)); + } + + using (var context = CreateContext()) + { + Assert.Equal(firstOperator.Name, context.Set().OrderBy(o => o.VehicleName).First().Name); + Assert.Equal(firstEngine.Description, context.Set().OrderBy(o => o.VehicleName).First().Description); + } + } + } + + [ConditionalFact] + public virtual void Can_query_shared() + { + using (CreateTestStore(OnModelCreating)) + { + using var context = CreateContext(); + Assert.Equal(5, context.Set().ToList().Count); + } + } + + [ConditionalFact] + public virtual void Can_query_shared_nonhierarchy() + { + using (CreateTestStore( + modelBuilder => + { + OnModelCreating(modelBuilder); + modelBuilder.Ignore(); + })) + { + using var context = CreateContext(); + Assert.Equal(5, context.Set().ToList().Count); + } + } + + [ConditionalFact] + public virtual void Can_query_shared_nonhierarchy_with_nonshared_dependent() + { + using (CreateTestStore( + modelBuilder => + { + OnModelCreating(modelBuilder); + modelBuilder.Ignore(); + modelBuilder.Entity().ToTable("OperatorDetails"); + })) + { + using var context = CreateContext(); + Assert.Equal(5, context.Set().ToList().Count); + } + } + + [ConditionalFact] + public virtual void Can_query_shared_derived_hierarchy() + { + using (CreateTestStore(OnModelCreating)) + { + using var context = CreateContext(); + Assert.Equal(2, context.Set().ToList().Count); + } + } + + [ConditionalFact] + public virtual void Can_query_shared_derived_nonhierarchy() + { + using (CreateTestStore( + modelBuilder => + { + OnModelCreating(modelBuilder); + modelBuilder.Ignore(); + })) + { + using var context = CreateContext(); + Assert.Equal(2, context.Set().ToList().Count); + } + } + + [ConditionalFact] + public virtual void Can_query_shared_derived_nonhierarchy_all_required() + { + using (CreateTestStore( + modelBuilder => + { + OnModelCreating(modelBuilder); + modelBuilder.Ignore(); + modelBuilder.Entity( + eb => + { + eb.Property(t => t.Capacity).IsRequired(); + eb.Property(t => t.FuelType).IsRequired(); + }); + })) + { + using var context = CreateContext(); + Assert.Equal(2, context.Set().ToList().Count); + } + } + + [ConditionalFact] + public virtual void Can_use_with_redundant_relationships() + { + Test_roundtrip(OnModelCreating); + } + + [ConditionalFact] + public virtual void Can_use_with_chained_relationships() + { + Test_roundtrip( + modelBuilder => + { + OnModelCreating(modelBuilder); + modelBuilder.Entity(eb => { eb.Ignore(e => e.Vehicle); }); + }); + } + + [ConditionalFact] + public virtual void Can_use_with_fanned_relationships() + { + Test_roundtrip( + modelBuilder => + { + OnModelCreating(modelBuilder); + + modelBuilder.Entity().HasOne(e => e.FuelTank).WithOne().HasForeignKey(e => e.VehicleName); + modelBuilder.Entity(eb => eb.Ignore(e => e.Engine)); + }); + } + + [ConditionalFact] + public virtual void Can_share_required_columns() + { + using (CreateTestStore( + modelBuilder => + { + OnModelCreating(modelBuilder); + modelBuilder.Entity( + vb => + { + vb.Property(v => v.SeatingCapacity).HasColumnName("SeatingCapacity"); + }); + modelBuilder.Entity( + cb => + { + cb.Property("SeatingCapacity").HasColumnName("SeatingCapacity"); + }); + + modelBuilder.Entity().HasOne(e => e.FuelTank).WithOne().HasForeignKey(e => e.VehicleName); + modelBuilder.Entity( + fb => + { + fb.Ignore(f => f.Engine); + }); + }, seed: false)) + { + using (var context = CreateContext()) + { + var scooterEntry = context.Add( + new PoweredVehicle + { + Name = "Electric scooter", + SeatingCapacity = 1, + Engine = new Engine() + }); + + scooterEntry.Reference(v => v.Engine).TargetEntry.Property("SeatingCapacity").CurrentValue = 1; + + context.SaveChanges(); + } + + using (var context = CreateContext()) + { + var scooter = context.Set().Include(v => v.Engine).Single(v => v.Name == "Electric scooter"); + + Assert.Equal(scooter.SeatingCapacity, context.Entry(scooter.Engine).Property("SeatingCapacity").CurrentValue); + } + } + } + + protected void Test_roundtrip(Action onModelCreating) + { + using (CreateTestStore(onModelCreating)) + { + using var context = CreateContext(); + context.AssertSeeded(); + } + } + + [ConditionalFact(Skip = "Issue#21497")] + public virtual void Can_manipulate_entities_sharing_row_independently() + { + using (CreateTestStore( + modelBuilder => + { + OnModelCreating(modelBuilder); + modelBuilder.Entity().HasOne(e => e.FuelTank).WithOne().HasForeignKey(e => e.VehicleName); + modelBuilder.Entity(eb => eb.Ignore(e => e.Engine)); + })) + { + PoweredVehicle streetcar; + using (var context = CreateContext()) + { + streetcar = context.Set().Include(v => v.Engine) + .Single(v => v.Name == "1984 California Car"); + + Assert.Null(streetcar.Engine); + + streetcar.Engine = new Engine { Description = "Streetcar engine" }; + + context.SaveChanges(); + } + + using (var context = CreateContext()) + { + var streetcarFromStore = context.Set().Include(v => v.Engine).AsNoTracking() + .Single(v => v.Name == "1984 California Car"); + + Assert.Equal("Streetcar engine", streetcarFromStore.Engine.Description); + + streetcarFromStore.Engine.Description = "Line"; + + context.Update(streetcarFromStore); + context.SaveChanges(); + } + + using (var context = CreateContext()) + { + var streetcarFromStore = context.Set().Include(v => v.Engine) + .Single(v => v.Name == "1984 California Car"); + + Assert.Equal("Line", streetcarFromStore.Engine.Description); + + streetcarFromStore.SeatingCapacity = 40; + streetcarFromStore.Engine.Description = "Streetcar engine"; + + context.SaveChanges(); + } + + using (var context = CreateContext()) + { + var streetcarFromStore = context.Set().Include(v => v.Engine).AsNoTracking() + .Single(v => v.Name == "1984 California Car"); + + Assert.Equal(40, streetcarFromStore.SeatingCapacity); + Assert.Equal("Streetcar engine", streetcarFromStore.Engine.Description); + + context.Remove(streetcarFromStore.Engine); + + context.SaveChanges(); + } + + using (var context = CreateContext()) + { + var streetcarFromStore = context.Set().Include(v => v.Engine).AsNoTracking() + .Single(v => v.Name == "1984 California Car"); + + Assert.Null(streetcarFromStore.Engine); + + context.Remove(streetcarFromStore); + + context.SaveChanges(); + } + + using (var context = CreateContext()) + { + Assert.Null(context.Set().AsNoTracking().SingleOrDefault(v => v.Name == "1984 California Car")); + Assert.Null(context.Set().AsNoTracking().SingleOrDefault(e => e.VehicleName == "1984 California Car")); + } + } + } + + [ConditionalFact] + public virtual void Can_insert_dependent_with_just_one_parent() + { + using (CreateTestStore(OnModelCreating)) + { + using var context = CreateContext(); + context.Add( + new PoweredVehicle + { + Name = "Fuel transport", + SeatingCapacity = 1, + Operator = new LicensedOperator { Name = "Jack Jackson", LicenseType = "Class A CDC" } + }); + context.Add( + new FuelTank + { + Capacity = "10000 l", + FuelType = "Gas", + VehicleName = "Fuel transport" + }); + + context.SaveChanges(); + } + } + + [ConditionalFact(Skip = "Issue#21497")] + public virtual void Can_change_dependent_instance_non_derived() + { + using (CreateTestStore( + modelBuilder => + { + OnModelCreating(modelBuilder); + modelBuilder.Entity().ToTable("Engines"); + modelBuilder.Entity( + eb => + { + eb.ToTable("FuelTanks"); + eb.HasOne(e => e.Engine) + .WithOne(e => e.FuelTank) + .HasForeignKey(e => e.VehicleName) + .OnDelete(DeleteBehavior.Restrict); + }); + modelBuilder.Ignore(); + modelBuilder.Ignore(); + })) + { + using (var context = CreateContext()) + { + var bike = context.Vehicles.Include(v => v.Operator).Single(v => v.Name == "Trek Pro Fit Madone 6 Series"); + + bike.Operator = new Operator { Name = "Chris Horner" }; + + context.ChangeTracker.DetectChanges(); + + bike.Operator = new LicensedOperator { Name = "repairman", LicenseType = "Repair" }; + + TestSqlLoggerFactory.Clear(); + context.SaveChanges(); + + Assert.Empty(context.ChangeTracker.Entries().Where(e => e.State != EntityState.Unchanged)); + } + + using (var context = CreateContext()) + { + var bike = context.Vehicles.Include(v => v.Operator).Single(v => v.Name == "Trek Pro Fit Madone 6 Series"); + Assert.Equal("repairman", bike.Operator.Name); + Assert.Equal("Repair", ((LicensedOperator)bike.Operator).LicenseType); + } + } + } + + [ConditionalFact] + public virtual void Can_change_principal_instance_non_derived() + { + using (CreateTestStore( + modelBuilder => + { + OnModelCreating(modelBuilder); + modelBuilder.Entity().ToTable("Engines"); + modelBuilder.Entity( + eb => + { + eb.ToTable("FuelTanks"); + eb.HasOne(e => e.Engine) + .WithOne(e => e.FuelTank) + .HasForeignKey(e => e.VehicleName) + .OnDelete(DeleteBehavior.Restrict); + }); + modelBuilder.Ignore(); + modelBuilder.Ignore(); + })) + { + using (var context = CreateContext()) + { + var bike = context.Vehicles.Single(v => v.Name == "Trek Pro Fit Madone 6 Series"); + + var newBike = new Vehicle + { + Name = "Trek Pro Fit Madone 6 Series", + Operator = bike.Operator, + SeatingCapacity = 2 + }; + + context.Remove(bike); + context.Add(newBike); + + TestSqlLoggerFactory.Clear(); + context.SaveChanges(); + + Assert.Empty(context.ChangeTracker.Entries().Where(e => e.State != EntityState.Unchanged)); + } + + using (var context = CreateContext()) + { + var bike = context.Vehicles.Include(v => v.Operator).Single(v => v.Name == "Trek Pro Fit Madone 6 Series"); + + Assert.Equal(2, bike.SeatingCapacity); + Assert.NotNull(bike.Operator); + } + } + } + + [ConditionalFact(Skip = "Issue#21497")] + public virtual void Can_change_principal_and_dependent_instance_non_derived() + { + using (CreateTestStore( + modelBuilder => + { + OnModelCreating(modelBuilder); + modelBuilder.Entity().ToTable("Engines"); + modelBuilder.Entity( + eb => + { + eb.ToTable("FuelTanks"); + eb.HasOne(e => e.Engine) + .WithOne(e => e.FuelTank) + .HasForeignKey(e => e.VehicleName) + .OnDelete(DeleteBehavior.Restrict); + }); + modelBuilder.Ignore(); + modelBuilder.Ignore(); + })) + { + using (var context = CreateContext()) + { + var bike = context.Vehicles.Include(v => v.Operator).Single(v => v.Name == "Trek Pro Fit Madone 6 Series"); + + var newBike = new Vehicle + { + Name = "Trek Pro Fit Madone 6 Series", + Operator = new LicensedOperator { Name = "repairman", LicenseType = "Repair" }, + SeatingCapacity = 2 + }; + + context.Remove(bike); + context.Add(newBike); + + TestSqlLoggerFactory.Clear(); + context.SaveChanges(); + + Assert.Empty(context.ChangeTracker.Entries().Where(e => e.State != EntityState.Unchanged)); + } + + using (var context = CreateContext()) + { + var bike = context.Vehicles.Include(v => v.Operator).Single(v => v.Name == "Trek Pro Fit Madone 6 Series"); + Assert.Equal(2, bike.SeatingCapacity); + Assert.Equal("repairman", bike.Operator.Name); + Assert.Equal("Repair", ((LicensedOperator)bike.Operator).LicenseType); + } + } + } + + protected readonly string DatabaseName = "TPTTableSplittingTest"; + protected TestStore TestStore { get; set; } + protected abstract ITestStoreFactory TestStoreFactory { get; } + protected IServiceProvider ServiceProvider { get; set; } + protected TestSqlLoggerFactory TestSqlLoggerFactory { get; } + + protected void AssertSql(params string[] expected) + => TestSqlLoggerFactory.AssertBaseline(expected); + + protected virtual void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().ToTable("Vehicles"); + modelBuilder.Entity().ToTable("PoweredVehicles"); + + modelBuilder.Entity().ToTable("Vehicles") + .Property("RequiredInt").HasDefaultValue(0); + modelBuilder.Entity().ToTable("LicensedOperators"); + + modelBuilder.Entity().ToTable("Vehicles"); + + modelBuilder.Entity().ToTable("PoweredVehicles") + .HasOne(e => e.Vehicle).WithOne(e => e.Engine).OnDelete(DeleteBehavior.NoAction); + modelBuilder.Entity().ToTable("CombustionEngines"); + modelBuilder.Entity().ToTable("IntermittentCombustionEngines"); + modelBuilder.Entity().ToTable("ContinuousCombustionEngines"); + modelBuilder.Entity().ToTable("SolidRockets").Ignore(e => e.SolidFuelTank); + + modelBuilder.Entity().ToTable("CombustionEngines") + .HasOne(e => e.Vehicle).WithOne().OnDelete(DeleteBehavior.NoAction); + modelBuilder.Entity().ToTable("SolidFuelTanks").Ignore(e => e.Rocket); + } + + protected TestStore CreateTestStore(Action onModelCreating, bool seed = true) + { + TestStore = TestStoreFactory.Create(DatabaseName); + + ServiceProvider = TestStoreFactory.AddProviderServices(new ServiceCollection()) + .AddSingleton(TestModelSource.GetFactory(onModelCreating)) + .AddSingleton(TestSqlLoggerFactory) + .BuildServiceProvider(validateScopes: true); + + TestStore.Initialize( + ServiceProvider, CreateContext, c => + { + if (seed) + { + ((TransportationContext)c).Seed(); + } + }); + + TestSqlLoggerFactory.Clear(); + + return TestStore; + } + + protected virtual DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) + => builder + .EnableSensitiveDataLogging() + .ConfigureWarnings( + b => b.Default(WarningBehavior.Throw) + .Log(CoreEventId.SensitiveDataLoggingEnabledWarning) + .Log(CoreEventId.PossibleUnintendedReferenceComparisonWarning)); + + protected virtual TransportationContext CreateContext() + { + var options = AddOptions(TestStore.AddProviderOptions(new DbContextOptionsBuilder())) + .UseInternalServiceProvider(ServiceProvider).Options; + return new TransportationContext(options); + } + } +} diff --git a/test/EFCore.Specification.Tests/Query/InheritanceQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/InheritanceQueryTestBase.cs index b30dc493a12..0f57c4114df 100644 --- a/test/EFCore.Specification.Tests/Query/InheritanceQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/InheritanceQueryTestBase.cs @@ -404,10 +404,6 @@ public virtual void Can_insert_update_delete() }); } - protected virtual void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction) - { - } - [ConditionalTheory(Skip = "Issue#16298")] [MemberData(nameof(IsAsyncData))] public virtual Task Union_siblings_with_duplicate_property_in_subquery(bool async) @@ -527,8 +523,24 @@ public virtual void Member_access_on_intermediate_type_works() Assert.Equal("Great spotted kiwi", kiwi.Name); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Is_operator_on_result_of_FirstOrDefault(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Where(a => ss.Set().FirstOrDefault(a1 => a1.Name == "Great spotted kiwi") is Kiwi) + .OrderBy(a => a.Species), + assertOrder: true); + } + protected InheritanceContext CreateContext() => Fixture.CreateContext(); + protected virtual void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction) + { + } + protected virtual bool EnforcesFkConstraints => true; protected virtual void ClearLog() diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index aa765286fa9..c45a0c89f95 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -3987,10 +3987,10 @@ public override async Task Correlated_collections_inner_subquery_selector_refere await base.Correlated_collections_inner_subquery_selector_references_outer_qsre(async); AssertSql( - @"SELECT [g].[FullName], [g].[Nickname], [g].[SquadId], [t].[FullName], [t].[FullName0], [t].[Nickname], [t].[SquadId] + @"SELECT [g].[FullName], [g].[Nickname], [g].[SquadId], [t].[ReportName], [t].[OfficerName], [t].[Nickname], [t].[SquadId] FROM [Gears] AS [g] OUTER APPLY ( - SELECT [g0].[FullName], [g].[FullName] AS [FullName0], [g0].[Nickname], [g0].[SquadId] + SELECT [g0].[FullName] AS [ReportName], [g].[FullName] AS [OfficerName], [g0].[Nickname], [g0].[SquadId] FROM [Gears] AS [g0] WHERE ([g].[Nickname] = [g0].[LeaderNickname]) AND ([g].[SquadId] = [g0].[LeaderSquadId]) ) AS [t] @@ -4003,10 +4003,10 @@ public override async Task Correlated_collections_inner_subquery_predicate_refer await base.Correlated_collections_inner_subquery_predicate_references_outer_qsre(async); AssertSql( - @"SELECT [g].[FullName], [g].[Nickname], [g].[SquadId], [t].[FullName], [t].[Nickname], [t].[SquadId] + @"SELECT [g].[FullName], [g].[Nickname], [g].[SquadId], [t].[ReportName], [t].[Nickname], [t].[SquadId] FROM [Gears] AS [g] OUTER APPLY ( - SELECT [g0].[FullName], [g0].[Nickname], [g0].[SquadId] + SELECT [g0].[FullName] AS [ReportName], [g0].[Nickname], [g0].[SquadId] FROM [Gears] AS [g0] WHERE ([g].[FullName] <> N'Foo') AND (([g].[Nickname] = [g0].[LeaderNickname]) AND ([g].[SquadId] = [g0].[LeaderSquadId])) ) AS [t] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/IncompleteMappingInheritanceQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/IncompleteMappingInheritanceQuerySqlServerTest.cs index 7c6cc3ac73a..55e73f4982f 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/IncompleteMappingInheritanceQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/IncompleteMappingInheritanceQuerySqlServerTest.cs @@ -525,6 +525,20 @@ INNER JOIN ( WHERE [a].[Discriminator] = N'Eagle'"); } + public override async Task Is_operator_on_result_of_FirstOrDefault(bool async) + { + await base.Is_operator_on_result_of_FirstOrDefault(async); + + AssertSql( + @"SELECT [a].[Species], [a].[CountryId], [a].[Discriminator], [a].[Name], [a].[EagleId], [a].[IsFlightless], [a].[Group], [a].[FoundOn] +FROM [Animal] AS [a] +WHERE [a].[Discriminator] IN (N'Eagle', N'Kiwi') AND (( + SELECT TOP(1) [a0].[Discriminator] + FROM [Animal] AS [a0] + WHERE [a0].[Discriminator] IN (N'Eagle', N'Kiwi') AND ([a0].[Name] = N'Great spotted kiwi')) = N'Kiwi') +ORDER BY [a].[Species]"); + } + protected override void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction) => facade.UseTransaction(transaction.GetDbTransaction()); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceQuerySqlServerTest.cs index aea867ae87d..a944a75780d 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceQuerySqlServerTest.cs @@ -501,6 +501,20 @@ INNER JOIN ( WHERE [a].[Discriminator] = N'Eagle'"); } + public override async Task Is_operator_on_result_of_FirstOrDefault(bool async) + { + await base.Is_operator_on_result_of_FirstOrDefault(async); + + AssertSql( + @"SELECT [a].[Species], [a].[CountryId], [a].[Discriminator], [a].[Name], [a].[EagleId], [a].[IsFlightless], [a].[Group], [a].[FoundOn] +FROM [Animal] AS [a] +WHERE ( + SELECT TOP(1) [a0].[Discriminator] + FROM [Animal] AS [a0] + WHERE [a0].[Name] = N'Great spotted kiwi') = N'Kiwi' +ORDER BY [a].[Species]"); + } + protected override void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction) => facade.UseTransaction(transaction.GetDbTransaction()); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs index 347fd6645e4..680334a23d6 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -4451,13 +4451,13 @@ public override async Task Complex_nested_query_doesnt_try_binding_to_grandparen await base.Complex_nested_query_doesnt_try_binding_to_grandparent_when_parent_returns_complex_result(async); AssertSql( - @"SELECT [c].[CustomerID], [t].[c], [t].[CustomerID], [t].[OrderID] + @"SELECT [c].[CustomerID], [t].[InnerOrder], [t].[Id], [t].[OrderID] FROM [Customers] AS [c] OUTER APPLY ( SELECT ( SELECT COUNT(*) FROM [Orders] AS [o] - WHERE [c].[CustomerID] = [o].[CustomerID]) AS [c], [c].[CustomerID], [o0].[OrderID] + WHERE [c].[CustomerID] = [o].[CustomerID]) AS [InnerOrder], [c].[CustomerID] AS [Id], [o0].[OrderID] FROM [Orders] AS [o0] WHERE [c].[CustomerID] = [o0].[CustomerID] ) AS [t] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs index 57fc1c1c949..96f85cfdca1 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs @@ -325,12 +325,12 @@ public override void Select_nested_collection_multi_level() base.Select_nested_collection_multi_level(); AssertSql( - @"SELECT [c].[CustomerID], [t0].[OrderDate], [t0].[OrderID] + @"SELECT [c].[CustomerID], [t0].[Date], [t0].[OrderID] FROM [Customers] AS [c] LEFT JOIN ( - SELECT [t].[OrderDate], [t].[OrderID], [t].[CustomerID] + SELECT [t].[Date], [t].[OrderID], [t].[CustomerID] FROM ( - SELECT [o].[OrderDate], [o].[OrderID], [o].[CustomerID], ROW_NUMBER() OVER(PARTITION BY [o].[CustomerID] ORDER BY [o].[OrderID]) AS [row] + SELECT [o].[OrderDate] AS [Date], [o].[OrderID], [o].[CustomerID], ROW_NUMBER() OVER(PARTITION BY [o].[CustomerID] ORDER BY [o].[OrderID]) AS [row] FROM [Orders] AS [o] WHERE [o].[OrderID] < 10500 ) AS [t] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs index 42b71fa451c..e3dea35c976 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs @@ -6361,7 +6361,7 @@ private class Activity12456 #region Issue15137 [ConditionalFact] - public virtual async Task Run_something() + public virtual async Task Max_in_multi_level_nested_subquery() { using (CreateDatabase15137()) { @@ -6391,8 +6391,8 @@ public virtual async Task Run_something() }) .SingleAsync(); - AssertSql( - @"SELECT [t0].[Id], [t1].[Id], [t1].[Id0], [t1].[Id1], [t1].[c] + AssertSql( + @"SELECT [t0].[Id], [t1].[Id], [t1].[Id0], [t1].[Id1], [t1].[IsPastTradeDeadline] FROM ( SELECT TOP(2) [t].[Id] FROM [Trades] AS [t] @@ -6404,7 +6404,7 @@ SELECT MAX([d].[GameNumber]) FROM [DbGame] AS [d] WHERE [d2].[Id] IS NOT NULL AND ([d2].[Id] = [d].[SeasonId])), 0) > 10 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) - END AS [c], [d0].[DbTradeId] + END AS [IsPastTradeDeadline], [d0].[DbTradeId] FROM [DbTradeAsset] AS [d0] INNER JOIN [DbContract] AS [d1] ON [d0].[ContractId] = [d1].[Id] LEFT JOIN [DbSeason] AS [d2] ON [d1].[SeasonId] = [d2].[Id] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTInheritanceQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTInheritanceQuerySqlServerTest.cs index e45970c608f..3cfe7634ff5 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTInheritanceQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTInheritanceQuerySqlServerTest.cs @@ -1,6 +1,9 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; using Xunit.Abstractions; // ReSharper disable InconsistentNaming @@ -14,5 +17,628 @@ public TPTInheritanceQuerySqlServerTest(TPTInheritanceQuerySqlServerFixture fixt Fixture.TestSqlLoggerFactory.Clear(); //Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } + + public override async Task Byte_enum_value_constant_used_in_projection(bool async) + { + await base.Byte_enum_value_constant_used_in_projection(async); + + AssertSql( + @"SELECT CASE + WHEN [b].[IsFlightless] = CAST(1 AS bit) THEN CAST(0 AS tinyint) + ELSE CAST(1 AS tinyint) +END +FROM [Animals] AS [a] +INNER JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +INNER JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species]"); + } + + public override async Task Can_filter_all_animals(bool async) + { + await base.Can_filter_all_animals(async); + + AssertSql( + @"SELECT [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [e].[Group], [k].[FoundOn], CASE + WHEN [e].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsEagle], CASE + WHEN [k].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsKiwi] +FROM [Animals] AS [a] +LEFT JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +LEFT JOIN [Eagle] AS [e] ON [a].[Species] = [e].[Species] +LEFT JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +WHERE [a].[Name] = N'Great spotted kiwi' +ORDER BY [a].[Species]"); + } + + public override async Task Can_include_animals(bool async) + { + await base.Can_include_animals(async); + + AssertSql( + @"SELECT [c].[Id], [c].[Name], [t].[Species], [t].[CountryId], [t].[Name], [t].[EagleId], [t].[IsFlightless], [t].[Group], [t].[FoundOn], [t].[IsEagle], [t].[IsKiwi] +FROM [Country] AS [c] +LEFT JOIN ( + SELECT [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [e].[Group], [k].[FoundOn], CASE + WHEN [e].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsEagle], CASE + WHEN [k].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsKiwi] + FROM [Animals] AS [a] + LEFT JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] + LEFT JOIN [Eagle] AS [e] ON [a].[Species] = [e].[Species] + LEFT JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +) AS [t] ON [c].[Id] = [t].[CountryId] +ORDER BY [c].[Name], [c].[Id], [t].[Species]"); + } + + public override async Task Can_include_prey(bool async) + { + await base.Can_include_prey(async); + + AssertSql( + @"SELECT [t].[Species], [t].[CountryId], [t].[Name], [t].[EagleId], [t].[IsFlightless], [t].[Group], [t0].[Species], [t0].[CountryId], [t0].[Name], [t0].[EagleId], [t0].[IsFlightless], [t0].[Group], [t0].[FoundOn], [t0].[IsEagle], [t0].[IsKiwi] +FROM ( + SELECT TOP(2) [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [e].[Group] + FROM [Animals] AS [a] + INNER JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] + INNER JOIN [Eagle] AS [e] ON [a].[Species] = [e].[Species] +) AS [t] +LEFT JOIN ( + SELECT [a0].[Species], [a0].[CountryId], [a0].[Name], [b0].[EagleId], [b0].[IsFlightless], [e0].[Group], [k].[FoundOn], CASE + WHEN [e0].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsEagle], CASE + WHEN [k].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsKiwi] + FROM [Animals] AS [a0] + INNER JOIN [Birds] AS [b0] ON [a0].[Species] = [b0].[Species] + LEFT JOIN [Eagle] AS [e0] ON [a0].[Species] = [e0].[Species] + LEFT JOIN [Kiwi] AS [k] ON [a0].[Species] = [k].[Species] +) AS [t0] ON [t].[Species] = [t0].[EagleId] +ORDER BY [t].[Species], [t0].[Species]"); + } + + public override void Can_insert_update_delete() + { + base.Can_insert_update_delete(); + + AssertSql( + @"SELECT TOP(2) [c].[Id], [c].[Name] +FROM [Countries] AS [c] +WHERE [c].[Id] = 1", + // + @"@p0='Apteryx owenii' (Nullable = false) (Size = 100) +@p1='1' +@p2='Little spotted kiwi' (Size = 4000) + +SET NOCOUNT ON; +INSERT INTO [Animals] ([Species], [CountryId], [Name]) +VALUES (@p0, @p1, @p2);", + // + @"@p0='Apteryx owenii' (Nullable = false) (Size = 100) +@p1=NULL (Size = 100) +@p2='True' + +SET NOCOUNT ON; +INSERT INTO [Birds] ([Species], [EagleId], [IsFlightless]) +VALUES (@p0, @p1, @p2);", + // + @"@p0='Apteryx owenii' (Nullable = false) (Size = 100) +@p1='0' (Size = 1) + +SET NOCOUNT ON; +INSERT INTO [Kiwi] ([Species], [FoundOn]) +VALUES (@p0, @p1);", + // + @"SELECT TOP(2) [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [k].[FoundOn] +FROM [Animals] AS [a] +INNER JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +INNER JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +WHERE [a].[Species] LIKE N'%owenii'", + // + @"@p1='Apteryx owenii' (Nullable = false) (Size = 100) +@p0='Aquila chrysaetos canadensis' (Size = 100) + +SET NOCOUNT ON; +UPDATE [Birds] SET [EagleId] = @p0 +WHERE [Species] = @p1; +SELECT @@ROWCOUNT;", + // + @"SELECT TOP(2) [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [k].[FoundOn] +FROM [Animals] AS [a] +INNER JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +INNER JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +WHERE [a].[Species] LIKE N'%owenii'", + // + @"@p0='Apteryx owenii' (Nullable = false) (Size = 100) + +SET NOCOUNT ON; +DELETE FROM [Animals] +WHERE [Species] = @p0; +SELECT @@ROWCOUNT;", + // + @"@p0='Apteryx owenii' (Nullable = false) (Size = 100) + +SET NOCOUNT ON; +DELETE FROM [Birds] +WHERE [Species] = @p0; +SELECT @@ROWCOUNT;", + // + @"@p0='Apteryx owenii' (Nullable = false) (Size = 100) + +SET NOCOUNT ON; +DELETE FROM [Kiwi] +WHERE [Species] = @p0; +SELECT @@ROWCOUNT;", + // + @"SELECT COUNT(*) +FROM [Animals] AS [a] +INNER JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +INNER JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +WHERE [a].[Species] LIKE N'%owenii'"); + } + + public override async Task Can_query_all_animals(bool async) + { + await base.Can_query_all_animals(async); + + AssertSql( + @"SELECT [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [e].[Group], [k].[FoundOn], CASE + WHEN [e].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsEagle], CASE + WHEN [k].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsKiwi] +FROM [Animals] AS [a] +LEFT JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +LEFT JOIN [Eagle] AS [e] ON [a].[Species] = [e].[Species] +LEFT JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +ORDER BY [a].[Species]"); + } + + public override async Task Can_query_all_birds(bool async) + { + await base.Can_query_all_birds(async); + + AssertSql( + @"SELECT [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [e].[Group], [k].[FoundOn], CASE + WHEN [e].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsEagle], CASE + WHEN [k].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsKiwi] +FROM [Animals] AS [a] +INNER JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +LEFT JOIN [Eagle] AS [e] ON [a].[Species] = [e].[Species] +LEFT JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +ORDER BY [a].[Species]"); + } + + public override async Task Can_query_all_plants(bool async) + { + await base.Can_query_all_plants(async); + + AssertSql( + @"SELECT [p].[Species], [p].[CountryId], [p].[Genus], [p].[Name], [r].[HasThorns], CASE + WHEN [d].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDaisy], CASE + WHEN [r].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsRose] +FROM [Plants] AS [p] +LEFT JOIN [Flowers] AS [f] ON [p].[Species] = [f].[Species] +LEFT JOIN [Daisies] AS [d] ON [p].[Species] = [d].[Species] +LEFT JOIN [Roses] AS [r] ON [p].[Species] = [r].[Species] +ORDER BY [p].[Species]"); + } + + public override async Task Can_query_all_types_when_shared_column(bool async) + { + await base.Can_query_all_types_when_shared_column(async); + + AssertSql( + @"SELECT [d].[Id], [c].[CaffeineGrams], [c].[CokeCO2], [c].[SugarGrams], [l].[LiltCO2], [l].[SugarGrams], [t].[CaffeineGrams], [t].[HasMilk], CASE + WHEN [c].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsCoke], CASE + WHEN [l].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsLilt], CASE + WHEN [t].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsTea] +FROM [Drinks] AS [d] +LEFT JOIN [Coke] AS [c] ON [d].[Id] = [c].[Id] +LEFT JOIN [Lilt] AS [l] ON [d].[Id] = [l].[Id] +LEFT JOIN [Tea] AS [t] ON [d].[Id] = [t].[Id]"); + } + + public override async Task Can_query_just_kiwis(bool async) + { + await base.Can_query_just_kiwis(async); + + AssertSql( + @"SELECT TOP(2) [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [k].[FoundOn] +FROM [Animals] AS [a] +INNER JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +INNER JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species]"); + } + + public override async Task Can_query_just_roses(bool async) + { + await base.Can_query_just_roses(async); + + AssertSql( + @"SELECT TOP(2) [p].[Species], [p].[CountryId], [p].[Genus], [p].[Name], [r].[HasThorns] +FROM [Plants] AS [p] +INNER JOIN [Flowers] AS [f] ON [p].[Species] = [f].[Species] +INNER JOIN [Roses] AS [r] ON [p].[Species] = [r].[Species]"); + } + + public override async Task Can_query_when_shared_column(bool async) + { + await base.Can_query_when_shared_column(async); + + AssertSql( + @"SELECT TOP(2) [d].[Id], [c].[CaffeineGrams], [c].[CokeCO2], [c].[SugarGrams] +FROM [Drinks] AS [d] +INNER JOIN [Coke] AS [c] ON [d].[Id] = [c].[Id]", + // + @"SELECT TOP(2) [d].[Id], [l].[LiltCO2], [l].[SugarGrams] +FROM [Drinks] AS [d] +INNER JOIN [Lilt] AS [l] ON [d].[Id] = [l].[Id]", + // + @"SELECT TOP(2) [d].[Id], [t].[CaffeineGrams], [t].[HasMilk] +FROM [Drinks] AS [d] +INNER JOIN [Tea] AS [t] ON [d].[Id] = [t].[Id]"); + } + + public override async Task Can_use_backwards_is_animal(bool async) + { + await base.Can_use_backwards_is_animal(async); + + AssertSql( + @"SELECT [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [k].[FoundOn] +FROM [Animals] AS [a] +INNER JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +INNER JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species]"); + } + + public override async Task Can_use_backwards_of_type_animal(bool async) + { + await base.Can_use_backwards_of_type_animal(async); + + AssertSql(" "); + } + + public override async Task Can_use_is_kiwi(bool async) + { + await base.Can_use_is_kiwi(async); + + AssertSql( + @"SELECT [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [e].[Group], [k].[FoundOn], CASE + WHEN [e].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsEagle], CASE + WHEN [k].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsKiwi] +FROM [Animals] AS [a] +LEFT JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +LEFT JOIN [Eagle] AS [e] ON [a].[Species] = [e].[Species] +LEFT JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +WHERE [k].[Species] IS NOT NULL"); + } + + public override async Task Can_use_is_kiwi_in_projection(bool async) + { + await base.Can_use_is_kiwi_in_projection(async); + + AssertSql( + @"SELECT CASE + WHEN [k].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END +FROM [Animals] AS [a] +LEFT JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +LEFT JOIN [Eagle] AS [e] ON [a].[Species] = [e].[Species] +LEFT JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species]"); + } + + public override async Task Can_use_is_kiwi_with_other_predicate(bool async) + { + await base.Can_use_is_kiwi_with_other_predicate(async); + + AssertSql( + @"SELECT [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [e].[Group], [k].[FoundOn], CASE + WHEN [e].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsEagle], CASE + WHEN [k].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsKiwi] +FROM [Animals] AS [a] +LEFT JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +LEFT JOIN [Eagle] AS [e] ON [a].[Species] = [e].[Species] +LEFT JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +WHERE [k].[Species] IS NOT NULL AND ([a].[CountryId] = 1)"); + } + + public override async Task Can_use_of_type_animal(bool async) + { + await base.Can_use_of_type_animal(async); + + AssertSql( + @"SELECT [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [e].[Group], [k].[FoundOn], CASE + WHEN [e].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsEagle], CASE + WHEN [k].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsKiwi] +FROM [Animals] AS [a] +LEFT JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +LEFT JOIN [Eagle] AS [e] ON [a].[Species] = [e].[Species] +LEFT JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +ORDER BY [a].[Species]"); + } + + public override async Task Can_use_of_type_bird(bool async) + { + await base.Can_use_of_type_bird(async); + + AssertSql( + @"SELECT [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [e].[Group], [k].[FoundOn], CASE + WHEN [e].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsEagle], CASE + WHEN [k].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsKiwi] +FROM [Animals] AS [a] +LEFT JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +LEFT JOIN [Eagle] AS [e] ON [a].[Species] = [e].[Species] +LEFT JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +WHERE [e].[Species] IS NOT NULL OR [k].[Species] IS NOT NULL +ORDER BY [a].[Species]"); + } + + public override async Task Can_use_of_type_bird_first(bool async) + { + await base.Can_use_of_type_bird_first(async); + + AssertSql( + @"SELECT TOP(1) [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [e].[Group], [k].[FoundOn], CASE + WHEN [e].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsEagle], CASE + WHEN [k].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsKiwi] +FROM [Animals] AS [a] +LEFT JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +LEFT JOIN [Eagle] AS [e] ON [a].[Species] = [e].[Species] +LEFT JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +WHERE [e].[Species] IS NOT NULL OR [k].[Species] IS NOT NULL +ORDER BY [a].[Species]"); + } + + public override async Task Can_use_of_type_bird_predicate(bool async) + { + await base.Can_use_of_type_bird_predicate(async); + + AssertSql( + @"SELECT [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [e].[Group], [k].[FoundOn], CASE + WHEN [e].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsEagle], CASE + WHEN [k].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsKiwi] +FROM [Animals] AS [a] +LEFT JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +LEFT JOIN [Eagle] AS [e] ON [a].[Species] = [e].[Species] +LEFT JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +WHERE ([a].[CountryId] = 1) AND ([e].[Species] IS NOT NULL OR [k].[Species] IS NOT NULL) +ORDER BY [a].[Species]"); + } + + public override async Task Can_use_of_type_bird_with_projection(bool async) + { + await base.Can_use_of_type_bird_with_projection(async); + + AssertSql( + @"SELECT [b].[EagleId] +FROM [Animals] AS [a] +LEFT JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +LEFT JOIN [Eagle] AS [e] ON [a].[Species] = [e].[Species] +LEFT JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +WHERE [e].[Species] IS NOT NULL OR [k].[Species] IS NOT NULL"); + } + + public override async Task Can_use_of_type_kiwi(bool async) + { + await base.Can_use_of_type_kiwi(async); + + AssertSql( + @"SELECT [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [k].[FoundOn] +FROM [Animals] AS [a] +LEFT JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +LEFT JOIN [Eagle] AS [e] ON [a].[Species] = [e].[Species] +LEFT JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +WHERE [k].[Species] IS NOT NULL"); + } + + public override async Task Can_use_of_type_kiwi_where_north_on_derived_property(bool async) + { + await base.Can_use_of_type_kiwi_where_north_on_derived_property(async); + + AssertSql( + @"SELECT [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [k].[FoundOn] +FROM [Animals] AS [a] +LEFT JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +LEFT JOIN [Eagle] AS [e] ON [a].[Species] = [e].[Species] +LEFT JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +WHERE [k].[Species] IS NOT NULL AND ([k].[FoundOn] = CAST(0 AS tinyint))"); + } + + public override async Task Can_use_of_type_kiwi_where_south_on_derived_property(bool async) + { + await base.Can_use_of_type_kiwi_where_south_on_derived_property(async); + + AssertSql( + @"SELECT [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [k].[FoundOn] +FROM [Animals] AS [a] +LEFT JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +LEFT JOIN [Eagle] AS [e] ON [a].[Species] = [e].[Species] +LEFT JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +WHERE [k].[Species] IS NOT NULL AND ([k].[FoundOn] = CAST(1 AS tinyint))"); + } + + public override async Task Can_use_of_type_rose(bool async) + { + await base.Can_use_of_type_rose(async); + + AssertSql( + @"SELECT [p].[Species], [p].[CountryId], [p].[Genus], [p].[Name], [r].[HasThorns] +FROM [Plants] AS [p] +LEFT JOIN [Flowers] AS [f] ON [p].[Species] = [f].[Species] +LEFT JOIN [Daisies] AS [d] ON [p].[Species] = [d].[Species] +LEFT JOIN [Roses] AS [r] ON [p].[Species] = [r].[Species] +WHERE [r].[Species] IS NOT NULL"); + } + + public override void Member_access_on_intermediate_type_works() + { + base.Member_access_on_intermediate_type_works(); + + AssertSql( + @"SELECT [a].[Name] +FROM [Animals] AS [a] +INNER JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +INNER JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +ORDER BY [a].[Name]"); + } + + public override async Task OfType_Union_OfType(bool async) + { + await base.OfType_Union_OfType(async); + + AssertSql(" "); + } + + public override async Task OfType_Union_subquery(bool async) + { + await base.OfType_Union_subquery(async); + + AssertSql(" "); + } + + public override void Setting_foreign_key_to_a_different_type_throws() + { + base.Setting_foreign_key_to_a_different_type_throws(); + + AssertSql( + @"SELECT TOP(2) [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [k].[FoundOn] +FROM [Animals] AS [a] +INNER JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +INNER JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species]", + // + @"@p0='Haliaeetus leucocephalus' (Nullable = false) (Size = 100) +@p1='0' +@p2='Bald eagle' (Size = 4000) + +SET NOCOUNT ON; +INSERT INTO [Animals] ([Species], [CountryId], [Name]) +VALUES (@p0, @p1, @p2);"); + } + + public override async Task Subquery_OfType(bool async) + { + await base.Subquery_OfType(async); + + AssertSql( + @"@__p_0='5' + +SELECT DISTINCT [t].[Species], [t].[CountryId], [t].[Name], [t].[EagleId], [t].[IsFlightless], [t].[FoundOn] +FROM ( + SELECT TOP(@__p_0) [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [e].[Group], [k].[FoundOn], CASE + WHEN [e].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsEagle], CASE + WHEN [k].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsKiwi] + FROM [Animals] AS [a] + INNER JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] + LEFT JOIN [Eagle] AS [e] ON [a].[Species] = [e].[Species] + LEFT JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +) AS [t] +WHERE [t].[IsKiwi] = CAST(1 AS bit)"); + } + + public override async Task Union_entity_equality(bool async) + { + await base.Union_entity_equality(async); + + AssertSql(" "); + } + + public override async Task Union_siblings_with_duplicate_property_in_subquery(bool async) + { + await base.Union_siblings_with_duplicate_property_in_subquery(async); + + AssertSql(" "); + } + + public override async Task Is_operator_on_result_of_FirstOrDefault(bool async) + { + await base.Is_operator_on_result_of_FirstOrDefault(async); + + AssertSql( + @"SELECT [a].[Species], [a].[CountryId], [a].[Name], [b].[EagleId], [b].[IsFlightless], [e].[Group], [k].[FoundOn], CASE + WHEN [e].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsEagle], CASE + WHEN [k].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsKiwi] +FROM [Animals] AS [a] +LEFT JOIN [Birds] AS [b] ON [a].[Species] = [b].[Species] +LEFT JOIN [Eagle] AS [e] ON [a].[Species] = [e].[Species] +LEFT JOIN [Kiwi] AS [k] ON [a].[Species] = [k].[Species] +WHERE EXISTS ( + SELECT 1 + FROM ( + SELECT TOP(1) [a0].[Species], [a0].[CountryId], [a0].[Name], [b0].[EagleId], [b0].[IsFlightless], [e0].[Group], [k0].[FoundOn], CASE + WHEN [e0].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsEagle], CASE + WHEN [k0].[Species] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsKiwi] + FROM [Animals] AS [a0] + LEFT JOIN [Birds] AS [b0] ON [a0].[Species] = [b0].[Species] + LEFT JOIN [Eagle] AS [e0] ON [a0].[Species] = [e0].[Species] + LEFT JOIN [Kiwi] AS [k0] ON [a0].[Species] = [k0].[Species] + WHERE [a0].[Name] = N'Great spotted kiwi' + ) AS [t] + WHERE [t].[IsKiwi] = CAST(1 AS bit)) +ORDER BY [a].[Species]"); + } + + protected override void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction) + => facade.UseTransaction(transaction.GetDbTransaction()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTRelationshipsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTRelationshipsQuerySqlServerTest.cs index d87c538eb59..bfe91888e2e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTRelationshipsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTRelationshipsQuerySqlServerTest.cs @@ -16,6 +16,1049 @@ public TPTRelationshipsQuerySqlServerTest( fixture.TestSqlLoggerFactory.Clear(); } + public override void Changes_in_derived_related_entities_are_detected() + { + base.Changes_in_derived_related_entities_are_detected(); + + AssertSql( + @"SELECT [t].[Id], [t].[Name], [t].[BaseId], [t].[IsDerivedInheritanceRelationshipEntity], [t].[OwnedReferenceOnBase_Id], [t].[OwnedReferenceOnBase_Name], [t].[Id0], [t].[OwnedReferenceOnDerived_Id], [t].[OwnedReferenceOnDerived_Name], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [d0].[Name], [t0].[Id], [t0].[BaseParentId], [t0].[Name], [t0].[DerivedProperty], [t0].[IsDerivedCollectionOnBase] +FROM ( + SELECT TOP(2) [b].[Id], [b].[Name], [d].[BaseId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedInheritanceRelationshipEntity], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id] AS [Id0], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name] + FROM [BaseEntities] AS [b] + LEFT JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] + WHERE [b].[Name] = N'Derived1(4)' +) AS [t] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b0] ON [t].[Id] = [b0].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d0] ON [t].[Id] = [d0].[DerivedInheritanceRelationshipEntityId] +LEFT JOIN ( + SELECT [b1].[Id], [b1].[BaseParentId], [b1].[Name], [d1].[DerivedProperty], CASE + WHEN [d1].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedCollectionOnBase] + FROM [BaseCollectionsOnBase] AS [b1] + LEFT JOIN [DerivedCollectionsOnBase] AS [d1] ON [b1].[Id] = [d1].[Id] +) AS [t0] ON [t].[Id] = [t0].[BaseParentId] +ORDER BY [t].[Id], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [t0].[Id]"); + } + + public override void Include_collection_without_inheritance() + { + base.Include_collection_without_inheritance(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedInheritanceRelationshipEntity], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [d0].[Name], [c].[Id], [c].[Name], [c].[ParentId] +FROM [BaseEntities] AS [b] +LEFT JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b0] ON [b].[Id] = [b0].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d0] ON [b].[Id] = [d0].[DerivedInheritanceRelationshipEntityId] +LEFT JOIN [CollectionsOnBase] AS [c] ON [b].[Id] = [c].[ParentId] +ORDER BY [b].[Id], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [c].[Id]"); + } + + public override void Include_collection_without_inheritance_reverse() + { + base.Include_collection_without_inheritance_reverse(); + + AssertSql( + @"SELECT [c].[Id], [c].[Name], [c].[ParentId], [t].[Id], [t].[Name], [t].[BaseId], [t].[IsDerivedInheritanceRelationshipEntity], [t].[Id0], [t].[OwnedReferenceOnBase_Id], [t].[OwnedReferenceOnBase_Name], [t].[Id1], [t].[OwnedReferenceOnDerived_Id], [t].[OwnedReferenceOnDerived_Name], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [d0].[Name] +FROM [CollectionsOnBase] AS [c] +LEFT JOIN ( + SELECT [b].[Id], [b].[Name], [d].[BaseId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedInheritanceRelationshipEntity], [b].[Id] AS [Id0], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id] AS [Id1], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name] + FROM [BaseEntities] AS [b] + LEFT JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +) AS [t] ON [c].[ParentId] = [t].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b0] ON [t].[Id] = [b0].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d0] ON [t].[Id] = [d0].[DerivedInheritanceRelationshipEntityId] +ORDER BY [c].[Id], [t].[Id], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id]"); + } + + public override void Include_collection_without_inheritance_with_filter() + { + base.Include_collection_without_inheritance_with_filter(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedInheritanceRelationshipEntity], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [d0].[Name], [c].[Id], [c].[Name], [c].[ParentId] +FROM [BaseEntities] AS [b] +LEFT JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b0] ON [b].[Id] = [b0].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d0] ON [b].[Id] = [d0].[DerivedInheritanceRelationshipEntityId] +LEFT JOIN [CollectionsOnBase] AS [c] ON [b].[Id] = [c].[ParentId] +WHERE ([b].[Name] <> N'Bar') OR [b].[Name] IS NULL +ORDER BY [b].[Id], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [c].[Id]"); + } + + public override void Include_collection_without_inheritance_with_filter_reverse() + { + base.Include_collection_without_inheritance_with_filter_reverse(); + + AssertSql( + @"SELECT [c].[Id], [c].[Name], [c].[ParentId], [t].[Id], [t].[Name], [t].[BaseId], [t].[IsDerivedInheritanceRelationshipEntity], [t].[Id0], [t].[OwnedReferenceOnBase_Id], [t].[OwnedReferenceOnBase_Name], [t].[Id1], [t].[OwnedReferenceOnDerived_Id], [t].[OwnedReferenceOnDerived_Name], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [d0].[Name] +FROM [CollectionsOnBase] AS [c] +LEFT JOIN ( + SELECT [b].[Id], [b].[Name], [d].[BaseId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedInheritanceRelationshipEntity], [b].[Id] AS [Id0], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id] AS [Id1], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name] + FROM [BaseEntities] AS [b] + LEFT JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +) AS [t] ON [c].[ParentId] = [t].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b0] ON [t].[Id] = [b0].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d0] ON [t].[Id] = [d0].[DerivedInheritanceRelationshipEntityId] +WHERE ([c].[Name] <> N'Bar') OR [c].[Name] IS NULL +ORDER BY [c].[Id], [t].[Id], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id]"); + } + + public override void Include_collection_with_inheritance() + { + base.Include_collection_with_inheritance(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedInheritanceRelationshipEntity], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [d0].[Name], [t].[Id], [t].[BaseParentId], [t].[Name], [t].[DerivedProperty], [t].[IsDerivedCollectionOnBase] +FROM [BaseEntities] AS [b] +LEFT JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b0] ON [b].[Id] = [b0].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d0] ON [b].[Id] = [d0].[DerivedInheritanceRelationshipEntityId] +LEFT JOIN ( + SELECT [b1].[Id], [b1].[BaseParentId], [b1].[Name], [d1].[DerivedProperty], CASE + WHEN [d1].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedCollectionOnBase] + FROM [BaseCollectionsOnBase] AS [b1] + LEFT JOIN [DerivedCollectionsOnBase] AS [d1] ON [b1].[Id] = [d1].[Id] +) AS [t] ON [b].[Id] = [t].[BaseParentId] +ORDER BY [b].[Id], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [t].[Id]"); + } + + public override void Include_collection_with_inheritance_on_derived1() + { + base.Include_collection_with_inheritance_on_derived1(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [d0].[Name], [t].[Id], [t].[BaseParentId], [t].[Name], [t].[DerivedProperty], [t].[IsDerivedCollectionOnBase] +FROM [BaseEntities] AS [b] +INNER JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b0] ON [b].[Id] = [b0].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d0] ON [b].[Id] = [d0].[DerivedInheritanceRelationshipEntityId] +LEFT JOIN ( + SELECT [b1].[Id], [b1].[BaseParentId], [b1].[Name], [d1].[DerivedProperty], CASE + WHEN [d1].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedCollectionOnBase] + FROM [BaseCollectionsOnBase] AS [b1] + LEFT JOIN [DerivedCollectionsOnBase] AS [d1] ON [b1].[Id] = [d1].[Id] +) AS [t] ON [b].[Id] = [t].[BaseParentId] +ORDER BY [b].[Id], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [t].[Id]"); + } + + public override void Include_collection_with_inheritance_on_derived2() + { + base.Include_collection_with_inheritance_on_derived2(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [d0].[Name], [t].[Id], [t].[Name], [t].[ParentId], [t].[DerivedInheritanceRelationshipEntityId], [t].[IsDerivedCollectionOnDerived] +FROM [BaseEntities] AS [b] +INNER JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b0] ON [b].[Id] = [b0].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d0] ON [b].[Id] = [d0].[DerivedInheritanceRelationshipEntityId] +LEFT JOIN ( + SELECT [b1].[Id], [b1].[Name], [b1].[ParentId], [d1].[DerivedInheritanceRelationshipEntityId], CASE + WHEN [d1].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedCollectionOnDerived] + FROM [BaseCollectionsOnDerived] AS [b1] + LEFT JOIN [DerivedCollectionsOnDerived] AS [d1] ON [b1].[Id] = [d1].[Id] +) AS [t] ON [b].[Id] = [t].[ParentId] +ORDER BY [b].[Id], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [t].[Id]"); + } + + public override void Include_collection_with_inheritance_on_derived3() + { + base.Include_collection_with_inheritance_on_derived3(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [d0].[Name], [t].[Id], [t].[Name], [t].[ParentId], [t].[DerivedInheritanceRelationshipEntityId] +FROM [BaseEntities] AS [b] +INNER JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b0] ON [b].[Id] = [b0].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d0] ON [b].[Id] = [d0].[DerivedInheritanceRelationshipEntityId] +LEFT JOIN ( + SELECT [b1].[Id], [b1].[Name], [b1].[ParentId], [d1].[DerivedInheritanceRelationshipEntityId] + FROM [BaseCollectionsOnDerived] AS [b1] + INNER JOIN [DerivedCollectionsOnDerived] AS [d1] ON [b1].[Id] = [d1].[Id] +) AS [t] ON [b].[Id] = [t].[DerivedInheritanceRelationshipEntityId] +ORDER BY [b].[Id], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [t].[Id]"); + } + + public override void Include_collection_with_inheritance_on_derived_reverse() + { + base.Include_collection_with_inheritance_on_derived_reverse(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [b].[ParentId], [d].[DerivedInheritanceRelationshipEntityId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedCollectionOnDerived], [t].[Id], [t].[Name], [t].[BaseId], [t].[Id0], [t].[OwnedReferenceOnBase_Id], [t].[OwnedReferenceOnBase_Name], [t].[Id1], [t].[OwnedReferenceOnDerived_Id], [t].[OwnedReferenceOnDerived_Name], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [BaseCollectionsOnDerived] AS [b] +LEFT JOIN [DerivedCollectionsOnDerived] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[Name], [d0].[BaseId], [b0].[Id] AS [Id0], [b0].[OwnedReferenceOnBase_Id], [b0].[OwnedReferenceOnBase_Name], [d0].[Id] AS [Id1], [d0].[OwnedReferenceOnDerived_Id], [d0].[OwnedReferenceOnDerived_Name] + FROM [BaseEntities] AS [b0] + INNER JOIN [DerivedEntities] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[ParentId] = [t].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [t].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [t].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +ORDER BY [b].[Id], [t].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Include_collection_with_inheritance_reverse() + { + base.Include_collection_with_inheritance_reverse(); + + AssertSql( + @"SELECT [b].[Id], [b].[BaseParentId], [b].[Name], [d].[DerivedProperty], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedCollectionOnBase], [t].[Id], [t].[Name], [t].[BaseId], [t].[IsDerivedInheritanceRelationshipEntity], [t].[Id0], [t].[OwnedReferenceOnBase_Id], [t].[OwnedReferenceOnBase_Name], [t].[Id1], [t].[OwnedReferenceOnDerived_Id], [t].[OwnedReferenceOnDerived_Name], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [BaseCollectionsOnBase] AS [b] +LEFT JOIN [DerivedCollectionsOnBase] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[Name], [d0].[BaseId], CASE + WHEN [d0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedInheritanceRelationshipEntity], [b0].[Id] AS [Id0], [b0].[OwnedReferenceOnBase_Id], [b0].[OwnedReferenceOnBase_Name], [d0].[Id] AS [Id1], [d0].[OwnedReferenceOnDerived_Id], [d0].[OwnedReferenceOnDerived_Name] + FROM [BaseEntities] AS [b0] + LEFT JOIN [DerivedEntities] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[BaseParentId] = [t].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [t].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [t].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +ORDER BY [b].[Id], [t].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Include_collection_with_inheritance_with_filter() + { + base.Include_collection_with_inheritance_with_filter(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedInheritanceRelationshipEntity], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [d0].[Name], [t].[Id], [t].[BaseParentId], [t].[Name], [t].[DerivedProperty], [t].[IsDerivedCollectionOnBase] +FROM [BaseEntities] AS [b] +LEFT JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b0] ON [b].[Id] = [b0].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d0] ON [b].[Id] = [d0].[DerivedInheritanceRelationshipEntityId] +LEFT JOIN ( + SELECT [b1].[Id], [b1].[BaseParentId], [b1].[Name], [d1].[DerivedProperty], CASE + WHEN [d1].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedCollectionOnBase] + FROM [BaseCollectionsOnBase] AS [b1] + LEFT JOIN [DerivedCollectionsOnBase] AS [d1] ON [b1].[Id] = [d1].[Id] +) AS [t] ON [b].[Id] = [t].[BaseParentId] +WHERE ([b].[Name] <> N'Bar') OR [b].[Name] IS NULL +ORDER BY [b].[Id], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [t].[Id]"); + } + + public override void Include_collection_with_inheritance_with_filter_reverse() + { + base.Include_collection_with_inheritance_with_filter_reverse(); + + AssertSql( + @"SELECT [b].[Id], [b].[BaseParentId], [b].[Name], [d].[DerivedProperty], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedCollectionOnBase], [t].[Id], [t].[Name], [t].[BaseId], [t].[IsDerivedInheritanceRelationshipEntity], [t].[Id0], [t].[OwnedReferenceOnBase_Id], [t].[OwnedReferenceOnBase_Name], [t].[Id1], [t].[OwnedReferenceOnDerived_Id], [t].[OwnedReferenceOnDerived_Name], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [BaseCollectionsOnBase] AS [b] +LEFT JOIN [DerivedCollectionsOnBase] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[Name], [d0].[BaseId], CASE + WHEN [d0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedInheritanceRelationshipEntity], [b0].[Id] AS [Id0], [b0].[OwnedReferenceOnBase_Id], [b0].[OwnedReferenceOnBase_Name], [d0].[Id] AS [Id1], [d0].[OwnedReferenceOnDerived_Id], [d0].[OwnedReferenceOnDerived_Name] + FROM [BaseEntities] AS [b0] + LEFT JOIN [DerivedEntities] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[BaseParentId] = [t].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [t].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [t].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +WHERE ([b].[Name] <> N'Bar') OR [b].[Name] IS NULL +ORDER BY [b].[Id], [t].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Include_reference_without_inheritance() + { + base.Include_reference_without_inheritance(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedInheritanceRelationshipEntity], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [r].[Id], [r].[Name], [r].[ParentId], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [d0].[Name] +FROM [BaseEntities] AS [b] +LEFT JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN [ReferencesOnBase] AS [r] ON [b].[Id] = [r].[ParentId] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b0] ON [b].[Id] = [b0].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d0] ON [b].[Id] = [d0].[DerivedInheritanceRelationshipEntityId] +ORDER BY [b].[Id], [r].[Id], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id]"); + } + + public override void Include_reference_without_inheritance_on_derived1() + { + base.Include_reference_without_inheritance_on_derived1(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [r].[Id], [r].[Name], [r].[ParentId], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [d0].[Name] +FROM [BaseEntities] AS [b] +INNER JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN [ReferencesOnBase] AS [r] ON [b].[Id] = [r].[ParentId] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b0] ON [b].[Id] = [b0].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d0] ON [b].[Id] = [d0].[DerivedInheritanceRelationshipEntityId] +ORDER BY [b].[Id], [r].[Id], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id]"); + } + + public override void Include_reference_without_inheritance_on_derived2() + { + base.Include_reference_without_inheritance_on_derived2(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [r].[Id], [r].[Name], [r].[ParentId], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [d0].[Name] +FROM [BaseEntities] AS [b] +INNER JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN [ReferencesOnDerived] AS [r] ON [b].[Id] = [r].[ParentId] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b0] ON [b].[Id] = [b0].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d0] ON [b].[Id] = [d0].[DerivedInheritanceRelationshipEntityId] +ORDER BY [b].[Id], [r].[Id], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id]"); + } + + public override void Include_reference_without_inheritance_on_derived_reverse() + { + base.Include_reference_without_inheritance_on_derived_reverse(); + + AssertSql( + @"SELECT [r].[Id], [r].[Name], [r].[ParentId], [t].[Id], [t].[Name], [t].[BaseId], [t].[Id0], [t].[OwnedReferenceOnBase_Id], [t].[OwnedReferenceOnBase_Name], [t].[Id1], [t].[OwnedReferenceOnDerived_Id], [t].[OwnedReferenceOnDerived_Name], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [d0].[Name] +FROM [ReferencesOnDerived] AS [r] +LEFT JOIN ( + SELECT [b].[Id], [b].[Name], [d].[BaseId], [b].[Id] AS [Id0], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id] AS [Id1], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name] + FROM [BaseEntities] AS [b] + INNER JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +) AS [t] ON [r].[ParentId] = [t].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b0] ON [t].[Id] = [b0].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d0] ON [t].[Id] = [d0].[DerivedInheritanceRelationshipEntityId] +ORDER BY [r].[Id], [t].[Id], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id]"); + } + + public override void Include_reference_without_inheritance_reverse() + { + base.Include_reference_without_inheritance_reverse(); + + AssertSql( + @"SELECT [r].[Id], [r].[Name], [r].[ParentId], [t].[Id], [t].[Name], [t].[BaseId], [t].[IsDerivedInheritanceRelationshipEntity], [t].[Id0], [t].[OwnedReferenceOnBase_Id], [t].[OwnedReferenceOnBase_Name], [t].[Id1], [t].[OwnedReferenceOnDerived_Id], [t].[OwnedReferenceOnDerived_Name], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [d0].[Name] +FROM [ReferencesOnBase] AS [r] +LEFT JOIN ( + SELECT [b].[Id], [b].[Name], [d].[BaseId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedInheritanceRelationshipEntity], [b].[Id] AS [Id0], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id] AS [Id1], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name] + FROM [BaseEntities] AS [b] + LEFT JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +) AS [t] ON [r].[ParentId] = [t].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b0] ON [t].[Id] = [b0].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d0] ON [t].[Id] = [d0].[DerivedInheritanceRelationshipEntityId] +ORDER BY [r].[Id], [t].[Id], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id]"); + } + + public override void Include_reference_without_inheritance_with_filter() + { + base.Include_reference_without_inheritance_with_filter(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedInheritanceRelationshipEntity], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [r].[Id], [r].[Name], [r].[ParentId], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [d0].[Name] +FROM [BaseEntities] AS [b] +LEFT JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN [ReferencesOnBase] AS [r] ON [b].[Id] = [r].[ParentId] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b0] ON [b].[Id] = [b0].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d0] ON [b].[Id] = [d0].[DerivedInheritanceRelationshipEntityId] +WHERE ([b].[Name] <> N'Bar') OR [b].[Name] IS NULL +ORDER BY [b].[Id], [r].[Id], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id]"); + } + + public override void Include_reference_without_inheritance_with_filter_reverse() + { + base.Include_reference_without_inheritance_with_filter_reverse(); + + AssertSql( + @"SELECT [r].[Id], [r].[Name], [r].[ParentId], [t].[Id], [t].[Name], [t].[BaseId], [t].[IsDerivedInheritanceRelationshipEntity], [t].[Id0], [t].[OwnedReferenceOnBase_Id], [t].[OwnedReferenceOnBase_Name], [t].[Id1], [t].[OwnedReferenceOnDerived_Id], [t].[OwnedReferenceOnDerived_Name], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [d0].[Name] +FROM [ReferencesOnBase] AS [r] +LEFT JOIN ( + SELECT [b].[Id], [b].[Name], [d].[BaseId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedInheritanceRelationshipEntity], [b].[Id] AS [Id0], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id] AS [Id1], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name] + FROM [BaseEntities] AS [b] + LEFT JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +) AS [t] ON [r].[ParentId] = [t].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b0] ON [t].[Id] = [b0].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d0] ON [t].[Id] = [d0].[DerivedInheritanceRelationshipEntityId] +WHERE ([r].[Name] <> N'Bar') OR [r].[Name] IS NULL +ORDER BY [r].[Id], [t].[Id], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id]"); + } + + public override void Include_reference_with_inheritance() + { + base.Include_reference_with_inheritance(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedInheritanceRelationshipEntity], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [t].[Id], [t].[BaseParentId], [t].[Name], [t].[IsDerivedReferenceOnBase], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [BaseEntities] AS [b] +LEFT JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[BaseParentId], [b0].[Name], CASE + WHEN [d0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedReferenceOnBase] + FROM [BaseReferencesOnBase] AS [b0] + LEFT JOIN [DerivedReferencesOnBase] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[Id] = [t].[BaseParentId] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [b].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [b].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +ORDER BY [b].[Id], [t].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Include_reference_with_inheritance_on_derived1() + { + base.Include_reference_with_inheritance_on_derived1(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [t].[Id], [t].[BaseParentId], [t].[Name], [t].[IsDerivedReferenceOnBase], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [BaseEntities] AS [b] +INNER JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[BaseParentId], [b0].[Name], CASE + WHEN [d0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedReferenceOnBase] + FROM [BaseReferencesOnBase] AS [b0] + LEFT JOIN [DerivedReferencesOnBase] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[Id] = [t].[BaseParentId] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [b].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [b].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +ORDER BY [b].[Id], [t].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Include_reference_with_inheritance_on_derived2() + { + base.Include_reference_with_inheritance_on_derived2(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [t].[Id], [t].[BaseParentId], [t].[Name], [t].[DerivedInheritanceRelationshipEntityId], [t].[IsDerivedReferenceOnDerived], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [BaseEntities] AS [b] +INNER JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[BaseParentId], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], CASE + WHEN [d0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedReferenceOnDerived] + FROM [BaseReferencesOnDerived] AS [b0] + LEFT JOIN [DerivedReferencesOnDerived] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[Id] = [t].[BaseParentId] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [b].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [b].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +ORDER BY [b].[Id], [t].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Include_reference_with_inheritance_on_derived4() + { + base.Include_reference_with_inheritance_on_derived4(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [t].[Id], [t].[BaseParentId], [t].[Name], [t].[DerivedInheritanceRelationshipEntityId], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [BaseEntities] AS [b] +INNER JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[BaseParentId], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId] + FROM [BaseReferencesOnDerived] AS [b0] + INNER JOIN [DerivedReferencesOnDerived] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[Id] = [t].[DerivedInheritanceRelationshipEntityId] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [b].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [b].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +ORDER BY [b].[Id], [t].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Include_reference_with_inheritance_on_derived_reverse() + { + base.Include_reference_with_inheritance_on_derived_reverse(); + + AssertSql( + @"SELECT [b].[Id], [b].[BaseParentId], [b].[Name], [d].[DerivedInheritanceRelationshipEntityId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedReferenceOnDerived], [t].[Id], [t].[Name], [t].[BaseId], [t].[Id0], [t].[OwnedReferenceOnBase_Id], [t].[OwnedReferenceOnBase_Name], [t].[Id1], [t].[OwnedReferenceOnDerived_Id], [t].[OwnedReferenceOnDerived_Name], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [BaseReferencesOnDerived] AS [b] +LEFT JOIN [DerivedReferencesOnDerived] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[Name], [d0].[BaseId], [b0].[Id] AS [Id0], [b0].[OwnedReferenceOnBase_Id], [b0].[OwnedReferenceOnBase_Name], [d0].[Id] AS [Id1], [d0].[OwnedReferenceOnDerived_Id], [d0].[OwnedReferenceOnDerived_Name] + FROM [BaseEntities] AS [b0] + INNER JOIN [DerivedEntities] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[BaseParentId] = [t].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [t].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [t].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +ORDER BY [b].[Id], [t].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Include_reference_with_inheritance_on_derived_with_filter1() + { + base.Include_reference_with_inheritance_on_derived_with_filter1(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [t].[Id], [t].[BaseParentId], [t].[Name], [t].[IsDerivedReferenceOnBase], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [BaseEntities] AS [b] +INNER JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[BaseParentId], [b0].[Name], CASE + WHEN [d0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedReferenceOnBase] + FROM [BaseReferencesOnBase] AS [b0] + LEFT JOIN [DerivedReferencesOnBase] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[Id] = [t].[BaseParentId] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [b].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [b].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +WHERE ([b].[Name] <> N'Bar') OR [b].[Name] IS NULL +ORDER BY [b].[Id], [t].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Include_reference_with_inheritance_on_derived_with_filter2() + { + base.Include_reference_with_inheritance_on_derived_with_filter2(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [t].[Id], [t].[BaseParentId], [t].[Name], [t].[DerivedInheritanceRelationshipEntityId], [t].[IsDerivedReferenceOnDerived], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [BaseEntities] AS [b] +INNER JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[BaseParentId], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], CASE + WHEN [d0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedReferenceOnDerived] + FROM [BaseReferencesOnDerived] AS [b0] + LEFT JOIN [DerivedReferencesOnDerived] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[Id] = [t].[BaseParentId] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [b].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [b].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +WHERE ([b].[Name] <> N'Bar') OR [b].[Name] IS NULL +ORDER BY [b].[Id], [t].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Include_reference_with_inheritance_on_derived_with_filter4() + { + base.Include_reference_with_inheritance_on_derived_with_filter4(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [t].[Id], [t].[BaseParentId], [t].[Name], [t].[DerivedInheritanceRelationshipEntityId], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [BaseEntities] AS [b] +INNER JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[BaseParentId], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId] + FROM [BaseReferencesOnDerived] AS [b0] + INNER JOIN [DerivedReferencesOnDerived] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[Id] = [t].[DerivedInheritanceRelationshipEntityId] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [b].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [b].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +WHERE ([b].[Name] <> N'Bar') OR [b].[Name] IS NULL +ORDER BY [b].[Id], [t].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Include_reference_with_inheritance_on_derived_with_filter_reverse() + { + base.Include_reference_with_inheritance_on_derived_with_filter_reverse(); + + AssertSql( + @"SELECT [b].[Id], [b].[BaseParentId], [b].[Name], [d].[DerivedInheritanceRelationshipEntityId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedReferenceOnDerived], [t].[Id], [t].[Name], [t].[BaseId], [t].[Id0], [t].[OwnedReferenceOnBase_Id], [t].[OwnedReferenceOnBase_Name], [t].[Id1], [t].[OwnedReferenceOnDerived_Id], [t].[OwnedReferenceOnDerived_Name], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [BaseReferencesOnDerived] AS [b] +LEFT JOIN [DerivedReferencesOnDerived] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[Name], [d0].[BaseId], [b0].[Id] AS [Id0], [b0].[OwnedReferenceOnBase_Id], [b0].[OwnedReferenceOnBase_Name], [d0].[Id] AS [Id1], [d0].[OwnedReferenceOnDerived_Id], [d0].[OwnedReferenceOnDerived_Name] + FROM [BaseEntities] AS [b0] + INNER JOIN [DerivedEntities] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[BaseParentId] = [t].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [t].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [t].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +WHERE ([b].[Name] <> N'Bar') OR [b].[Name] IS NULL +ORDER BY [b].[Id], [t].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Include_reference_with_inheritance_reverse() + { + base.Include_reference_with_inheritance_reverse(); + + AssertSql( + @"SELECT [b].[Id], [b].[BaseParentId], [b].[Name], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedReferenceOnBase], [t].[Id], [t].[Name], [t].[BaseId], [t].[IsDerivedInheritanceRelationshipEntity], [t].[Id0], [t].[OwnedReferenceOnBase_Id], [t].[OwnedReferenceOnBase_Name], [t].[Id1], [t].[OwnedReferenceOnDerived_Id], [t].[OwnedReferenceOnDerived_Name], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [BaseReferencesOnBase] AS [b] +LEFT JOIN [DerivedReferencesOnBase] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[Name], [d0].[BaseId], CASE + WHEN [d0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedInheritanceRelationshipEntity], [b0].[Id] AS [Id0], [b0].[OwnedReferenceOnBase_Id], [b0].[OwnedReferenceOnBase_Name], [d0].[Id] AS [Id1], [d0].[OwnedReferenceOnDerived_Id], [d0].[OwnedReferenceOnDerived_Name] + FROM [BaseEntities] AS [b0] + LEFT JOIN [DerivedEntities] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[BaseParentId] = [t].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [t].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [t].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +ORDER BY [b].[Id], [t].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Include_reference_with_inheritance_with_filter() + { + base.Include_reference_with_inheritance_with_filter(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedInheritanceRelationshipEntity], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [t].[Id], [t].[BaseParentId], [t].[Name], [t].[IsDerivedReferenceOnBase], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [BaseEntities] AS [b] +LEFT JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[BaseParentId], [b0].[Name], CASE + WHEN [d0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedReferenceOnBase] + FROM [BaseReferencesOnBase] AS [b0] + LEFT JOIN [DerivedReferencesOnBase] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[Id] = [t].[BaseParentId] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [b].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [b].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +WHERE ([b].[Name] <> N'Bar') OR [b].[Name] IS NULL +ORDER BY [b].[Id], [t].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Include_reference_with_inheritance_with_filter_reverse() + { + base.Include_reference_with_inheritance_with_filter_reverse(); + + AssertSql( + @"SELECT [b].[Id], [b].[BaseParentId], [b].[Name], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedReferenceOnBase], [t].[Id], [t].[Name], [t].[BaseId], [t].[IsDerivedInheritanceRelationshipEntity], [t].[Id0], [t].[OwnedReferenceOnBase_Id], [t].[OwnedReferenceOnBase_Name], [t].[Id1], [t].[OwnedReferenceOnDerived_Id], [t].[OwnedReferenceOnDerived_Name], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [BaseReferencesOnBase] AS [b] +LEFT JOIN [DerivedReferencesOnBase] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[Name], [d0].[BaseId], CASE + WHEN [d0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedInheritanceRelationshipEntity], [b0].[Id] AS [Id0], [b0].[OwnedReferenceOnBase_Id], [b0].[OwnedReferenceOnBase_Name], [d0].[Id] AS [Id1], [d0].[OwnedReferenceOnDerived_Id], [d0].[OwnedReferenceOnDerived_Name] + FROM [BaseEntities] AS [b0] + LEFT JOIN [DerivedEntities] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[BaseParentId] = [t].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [t].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [t].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +WHERE ([b].[Name] <> N'Bar') OR [b].[Name] IS NULL +ORDER BY [b].[Id], [t].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Include_self_reference_with_inheritance() + { + base.Include_self_reference_with_inheritance(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedInheritanceRelationshipEntity], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [t].[Id], [t].[Name], [t].[BaseId], [t].[Id0], [t].[OwnedReferenceOnBase_Id], [t].[OwnedReferenceOnBase_Name], [t].[Id1], [t].[OwnedReferenceOnDerived_Id], [t].[OwnedReferenceOnDerived_Name], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name], [b2].[BaseInheritanceRelationshipEntityId], [b2].[Id], [b2].[Name], [d2].[DerivedInheritanceRelationshipEntityId], [d2].[Id], [d2].[Name] +FROM [BaseEntities] AS [b] +LEFT JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[Name], [d0].[BaseId], [b0].[Id] AS [Id0], [b0].[OwnedReferenceOnBase_Id], [b0].[OwnedReferenceOnBase_Name], [d0].[Id] AS [Id1], [d0].[OwnedReferenceOnDerived_Id], [d0].[OwnedReferenceOnDerived_Name] + FROM [BaseEntities] AS [b0] + INNER JOIN [DerivedEntities] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[Id] = [t].[BaseId] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [b].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [b].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b2] ON [t].[Id] = [b2].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d2] ON [t].[Id] = [d2].[DerivedInheritanceRelationshipEntityId] +ORDER BY [b].[Id], [t].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [b2].[BaseInheritanceRelationshipEntityId], [b2].[Id], [d2].[DerivedInheritanceRelationshipEntityId], [d2].[Id]"); + } + + public override void Include_self_reference_with_inheritance_reverse() + { + base.Include_self_reference_with_inheritance_reverse(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [t].[Id], [t].[Name], [t].[BaseId], [t].[IsDerivedInheritanceRelationshipEntity], [t].[Id0], [t].[OwnedReferenceOnBase_Id], [t].[OwnedReferenceOnBase_Name], [t].[Id1], [t].[OwnedReferenceOnDerived_Id], [t].[OwnedReferenceOnDerived_Name], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name], [b2].[BaseInheritanceRelationshipEntityId], [b2].[Id], [b2].[Name], [d2].[DerivedInheritanceRelationshipEntityId], [d2].[Id], [d2].[Name] +FROM [BaseEntities] AS [b] +INNER JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[Name], [d0].[BaseId], CASE + WHEN [d0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedInheritanceRelationshipEntity], [b0].[Id] AS [Id0], [b0].[OwnedReferenceOnBase_Id], [b0].[OwnedReferenceOnBase_Name], [d0].[Id] AS [Id1], [d0].[OwnedReferenceOnDerived_Id], [d0].[OwnedReferenceOnDerived_Name] + FROM [BaseEntities] AS [b0] + LEFT JOIN [DerivedEntities] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [d].[BaseId] = [t].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [b].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [b].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b2] ON [t].[Id] = [b2].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d2] ON [t].[Id] = [d2].[DerivedInheritanceRelationshipEntityId] +ORDER BY [b].[Id], [t].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [b2].[BaseInheritanceRelationshipEntityId], [b2].[Id], [d2].[DerivedInheritanceRelationshipEntityId], [d2].[Id]"); + } + + public override void Nested_include_collection_reference_on_non_entity_base() + { + base.Nested_include_collection_reference_on_non_entity_base(); + + AssertSql( + @"SELECT [r].[Id], [r].[Name], [t].[Id], [t].[Name], [t].[ReferenceId], [t].[ReferencedEntityId], [t].[Id0], [t].[Name0] +FROM [ReferencedEntities] AS [r] +LEFT JOIN ( + SELECT [p].[Id], [p].[Name], [p].[ReferenceId], [p].[ReferencedEntityId], [r0].[Id] AS [Id0], [r0].[Name] AS [Name0] + FROM [PrincipalEntities] AS [p] + LEFT JOIN [ReferencedEntities] AS [r0] ON [p].[ReferenceId] = [r0].[Id] +) AS [t] ON [r].[Id] = [t].[ReferencedEntityId] +ORDER BY [r].[Id], [t].[Id], [t].[Id0]"); + } + + public override void Nested_include_with_inheritance_collection_collection() + { + base.Nested_include_with_inheritance_collection_collection(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedInheritanceRelationshipEntity], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [d0].[Name], [t0].[Id], [t0].[BaseParentId], [t0].[Name], [t0].[DerivedProperty], [t0].[IsDerivedCollectionOnBase], [t0].[Id0], [t0].[Name0], [t0].[ParentCollectionId], [t0].[ParentReferenceId], [t0].[IsNestedCollectionDerived] +FROM [BaseEntities] AS [b] +LEFT JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b0] ON [b].[Id] = [b0].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d0] ON [b].[Id] = [d0].[DerivedInheritanceRelationshipEntityId] +LEFT JOIN ( + SELECT [b1].[Id], [b1].[BaseParentId], [b1].[Name], [d1].[DerivedProperty], CASE + WHEN [d1].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedCollectionOnBase], [t].[Id] AS [Id0], [t].[Name] AS [Name0], [t].[ParentCollectionId], [t].[ParentReferenceId], [t].[IsNestedCollectionDerived] + FROM [BaseCollectionsOnBase] AS [b1] + LEFT JOIN [DerivedCollectionsOnBase] AS [d1] ON [b1].[Id] = [d1].[Id] + LEFT JOIN ( + SELECT [n].[Id], [n].[Name], [n].[ParentCollectionId], [n].[ParentReferenceId], CASE + WHEN [n0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsNestedCollectionDerived] + FROM [NestedCollections] AS [n] + LEFT JOIN [NestedCollectionsDerived] AS [n0] ON [n].[Id] = [n0].[Id] + ) AS [t] ON [b1].[Id] = [t].[ParentCollectionId] +) AS [t0] ON [b].[Id] = [t0].[BaseParentId] +ORDER BY [b].[Id], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [t0].[Id], [t0].[Id0]"); + } + + public override void Nested_include_with_inheritance_collection_collection_reverse() + { + base.Nested_include_with_inheritance_collection_collection_reverse(); + + AssertSql( + @"SELECT [n].[Id], [n].[Name], [n].[ParentCollectionId], [n].[ParentReferenceId], CASE + WHEN [n0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsNestedCollectionDerived], [t].[Id], [t].[BaseParentId], [t].[Name], [t].[DerivedProperty], [t].[IsDerivedCollectionOnBase], [t0].[Id], [t0].[Name], [t0].[BaseId], [t0].[IsDerivedInheritanceRelationshipEntity], [t0].[Id0], [t0].[OwnedReferenceOnBase_Id], [t0].[OwnedReferenceOnBase_Name], [t0].[Id1], [t0].[OwnedReferenceOnDerived_Id], [t0].[OwnedReferenceOnDerived_Name], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [NestedCollections] AS [n] +LEFT JOIN [NestedCollectionsDerived] AS [n0] ON [n].[Id] = [n0].[Id] +LEFT JOIN ( + SELECT [b].[Id], [b].[BaseParentId], [b].[Name], [d].[DerivedProperty], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedCollectionOnBase] + FROM [BaseCollectionsOnBase] AS [b] + LEFT JOIN [DerivedCollectionsOnBase] AS [d] ON [b].[Id] = [d].[Id] +) AS [t] ON [n].[ParentCollectionId] = [t].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[Name], [d0].[BaseId], CASE + WHEN [d0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedInheritanceRelationshipEntity], [b0].[Id] AS [Id0], [b0].[OwnedReferenceOnBase_Id], [b0].[OwnedReferenceOnBase_Name], [d0].[Id] AS [Id1], [d0].[OwnedReferenceOnDerived_Id], [d0].[OwnedReferenceOnDerived_Name] + FROM [BaseEntities] AS [b0] + LEFT JOIN [DerivedEntities] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t0] ON [t].[BaseParentId] = [t0].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [t0].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [t0].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +ORDER BY [n].[Id], [t].[Id], [t0].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Nested_include_with_inheritance_collection_reference() + { + base.Nested_include_with_inheritance_collection_reference(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedInheritanceRelationshipEntity], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [b0].[Name], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [d0].[Name], [t0].[Id], [t0].[BaseParentId], [t0].[Name], [t0].[DerivedProperty], [t0].[IsDerivedCollectionOnBase], [t0].[Id0], [t0].[Name0], [t0].[ParentCollectionId], [t0].[ParentReferenceId], [t0].[IsNestedReferenceDerived] +FROM [BaseEntities] AS [b] +LEFT JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b0] ON [b].[Id] = [b0].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d0] ON [b].[Id] = [d0].[DerivedInheritanceRelationshipEntityId] +LEFT JOIN ( + SELECT [b1].[Id], [b1].[BaseParentId], [b1].[Name], [d1].[DerivedProperty], CASE + WHEN [d1].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedCollectionOnBase], [t].[Id] AS [Id0], [t].[Name] AS [Name0], [t].[ParentCollectionId], [t].[ParentReferenceId], [t].[IsNestedReferenceDerived] + FROM [BaseCollectionsOnBase] AS [b1] + LEFT JOIN [DerivedCollectionsOnBase] AS [d1] ON [b1].[Id] = [d1].[Id] + LEFT JOIN ( + SELECT [n].[Id], [n].[Name], [n].[ParentCollectionId], [n].[ParentReferenceId], CASE + WHEN [n0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsNestedReferenceDerived] + FROM [NestedReferences] AS [n] + LEFT JOIN [NestedReferencesDerived] AS [n0] ON [n].[Id] = [n0].[Id] + ) AS [t] ON [b1].[Id] = [t].[ParentCollectionId] +) AS [t0] ON [b].[Id] = [t0].[BaseParentId] +ORDER BY [b].[Id], [b0].[BaseInheritanceRelationshipEntityId], [b0].[Id], [d0].[DerivedInheritanceRelationshipEntityId], [d0].[Id], [t0].[Id], [t0].[Id0]"); + } + + public override void Nested_include_with_inheritance_collection_reference_reverse() + { + base.Nested_include_with_inheritance_collection_reference_reverse(); + + AssertSql( + @"SELECT [n].[Id], [n].[Name], [n].[ParentCollectionId], [n].[ParentReferenceId], CASE + WHEN [n0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsNestedReferenceDerived], [t].[Id], [t].[BaseParentId], [t].[Name], [t].[DerivedProperty], [t].[IsDerivedCollectionOnBase], [t0].[Id], [t0].[Name], [t0].[BaseId], [t0].[IsDerivedInheritanceRelationshipEntity], [t0].[Id0], [t0].[OwnedReferenceOnBase_Id], [t0].[OwnedReferenceOnBase_Name], [t0].[Id1], [t0].[OwnedReferenceOnDerived_Id], [t0].[OwnedReferenceOnDerived_Name], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [NestedReferences] AS [n] +LEFT JOIN [NestedReferencesDerived] AS [n0] ON [n].[Id] = [n0].[Id] +LEFT JOIN ( + SELECT [b].[Id], [b].[BaseParentId], [b].[Name], [d].[DerivedProperty], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedCollectionOnBase] + FROM [BaseCollectionsOnBase] AS [b] + LEFT JOIN [DerivedCollectionsOnBase] AS [d] ON [b].[Id] = [d].[Id] +) AS [t] ON [n].[ParentCollectionId] = [t].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[Name], [d0].[BaseId], CASE + WHEN [d0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedInheritanceRelationshipEntity], [b0].[Id] AS [Id0], [b0].[OwnedReferenceOnBase_Id], [b0].[OwnedReferenceOnBase_Name], [d0].[Id] AS [Id1], [d0].[OwnedReferenceOnDerived_Id], [d0].[OwnedReferenceOnDerived_Name] + FROM [BaseEntities] AS [b0] + LEFT JOIN [DerivedEntities] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t0] ON [t].[BaseParentId] = [t0].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [t0].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [t0].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +ORDER BY [n].[Id], [t].[Id], [t0].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Nested_include_with_inheritance_reference_collection() + { + base.Nested_include_with_inheritance_reference_collection(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedInheritanceRelationshipEntity], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [t].[Id], [t].[BaseParentId], [t].[Name], [t].[IsDerivedReferenceOnBase], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name], [t0].[Id], [t0].[Name], [t0].[ParentCollectionId], [t0].[ParentReferenceId], [t0].[IsNestedCollectionDerived] +FROM [BaseEntities] AS [b] +LEFT JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[BaseParentId], [b0].[Name], CASE + WHEN [d0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedReferenceOnBase] + FROM [BaseReferencesOnBase] AS [b0] + LEFT JOIN [DerivedReferencesOnBase] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[Id] = [t].[BaseParentId] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [b].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [b].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +LEFT JOIN ( + SELECT [n].[Id], [n].[Name], [n].[ParentCollectionId], [n].[ParentReferenceId], CASE + WHEN [n0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsNestedCollectionDerived] + FROM [NestedCollections] AS [n] + LEFT JOIN [NestedCollectionsDerived] AS [n0] ON [n].[Id] = [n0].[Id] +) AS [t0] ON [t].[Id] = [t0].[ParentReferenceId] +ORDER BY [b].[Id], [t].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [t0].[Id]"); + } + + public override void Nested_include_with_inheritance_reference_collection_on_base() + { + base.Nested_include_with_inheritance_reference_collection_on_base(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [t].[Id], [t].[BaseParentId], [t].[Name], [t].[IsDerivedReferenceOnBase], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name], [t0].[Id], [t0].[Name], [t0].[ParentCollectionId], [t0].[ParentReferenceId], [t0].[IsNestedCollectionDerived] +FROM [BaseEntities] AS [b] +INNER JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[BaseParentId], [b0].[Name], CASE + WHEN [d0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedReferenceOnBase] + FROM [BaseReferencesOnBase] AS [b0] + LEFT JOIN [DerivedReferencesOnBase] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[Id] = [t].[BaseParentId] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [b].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [b].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +LEFT JOIN ( + SELECT [n].[Id], [n].[Name], [n].[ParentCollectionId], [n].[ParentReferenceId], CASE + WHEN [n0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsNestedCollectionDerived] + FROM [NestedCollections] AS [n] + LEFT JOIN [NestedCollectionsDerived] AS [n0] ON [n].[Id] = [n0].[Id] +) AS [t0] ON [t].[Id] = [t0].[ParentReferenceId] +ORDER BY [b].[Id], [t].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [t0].[Id]"); + } + + public override void Nested_include_with_inheritance_reference_collection_reverse() + { + base.Nested_include_with_inheritance_reference_collection_reverse(); + + AssertSql( + @"SELECT [n].[Id], [n].[Name], [n].[ParentCollectionId], [n].[ParentReferenceId], CASE + WHEN [n0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsNestedCollectionDerived], [t].[Id], [t].[BaseParentId], [t].[Name], [t].[IsDerivedReferenceOnBase], [t0].[Id], [t0].[Name], [t0].[BaseId], [t0].[IsDerivedInheritanceRelationshipEntity], [t0].[Id0], [t0].[OwnedReferenceOnBase_Id], [t0].[OwnedReferenceOnBase_Name], [t0].[Id1], [t0].[OwnedReferenceOnDerived_Id], [t0].[OwnedReferenceOnDerived_Name], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [NestedCollections] AS [n] +LEFT JOIN [NestedCollectionsDerived] AS [n0] ON [n].[Id] = [n0].[Id] +LEFT JOIN ( + SELECT [b].[Id], [b].[BaseParentId], [b].[Name], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedReferenceOnBase] + FROM [BaseReferencesOnBase] AS [b] + LEFT JOIN [DerivedReferencesOnBase] AS [d] ON [b].[Id] = [d].[Id] +) AS [t] ON [n].[ParentReferenceId] = [t].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[Name], [d0].[BaseId], CASE + WHEN [d0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedInheritanceRelationshipEntity], [b0].[Id] AS [Id0], [b0].[OwnedReferenceOnBase_Id], [b0].[OwnedReferenceOnBase_Name], [d0].[Id] AS [Id1], [d0].[OwnedReferenceOnDerived_Id], [d0].[OwnedReferenceOnDerived_Name] + FROM [BaseEntities] AS [b0] + LEFT JOIN [DerivedEntities] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t0] ON [t].[BaseParentId] = [t0].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [t0].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [t0].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +ORDER BY [n].[Id], [t].[Id], [t0].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Nested_include_with_inheritance_reference_reference() + { + base.Nested_include_with_inheritance_reference_reference(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsDerivedInheritanceRelationshipEntity], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [t].[Id], [t].[BaseParentId], [t].[Name], [t].[IsDerivedReferenceOnBase], [t0].[Id], [t0].[Name], [t0].[ParentCollectionId], [t0].[ParentReferenceId], [t0].[IsNestedReferenceDerived], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [BaseEntities] AS [b] +LEFT JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[BaseParentId], [b0].[Name], CASE + WHEN [d0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedReferenceOnBase] + FROM [BaseReferencesOnBase] AS [b0] + LEFT JOIN [DerivedReferencesOnBase] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[Id] = [t].[BaseParentId] +LEFT JOIN ( + SELECT [n].[Id], [n].[Name], [n].[ParentCollectionId], [n].[ParentReferenceId], CASE + WHEN [n0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsNestedReferenceDerived] + FROM [NestedReferences] AS [n] + LEFT JOIN [NestedReferencesDerived] AS [n0] ON [n].[Id] = [n0].[Id] +) AS [t0] ON [t].[Id] = [t0].[ParentReferenceId] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [b].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [b].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +ORDER BY [b].[Id], [t].[Id], [t0].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Nested_include_with_inheritance_reference_reference_on_base() + { + base.Nested_include_with_inheritance_reference_reference_on_base(); + + AssertSql( + @"SELECT [b].[Id], [b].[Name], [d].[BaseId], [b].[OwnedReferenceOnBase_Id], [b].[OwnedReferenceOnBase_Name], [d].[Id], [d].[OwnedReferenceOnDerived_Id], [d].[OwnedReferenceOnDerived_Name], [t].[Id], [t].[BaseParentId], [t].[Name], [t].[IsDerivedReferenceOnBase], [t0].[Id], [t0].[Name], [t0].[ParentCollectionId], [t0].[ParentReferenceId], [t0].[IsNestedReferenceDerived], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [BaseEntities] AS [b] +INNER JOIN [DerivedEntities] AS [d] ON [b].[Id] = [d].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[BaseParentId], [b0].[Name], CASE + WHEN [d0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedReferenceOnBase] + FROM [BaseReferencesOnBase] AS [b0] + LEFT JOIN [DerivedReferencesOnBase] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t] ON [b].[Id] = [t].[BaseParentId] +LEFT JOIN ( + SELECT [n].[Id], [n].[Name], [n].[ParentCollectionId], [n].[ParentReferenceId], CASE + WHEN [n0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsNestedReferenceDerived] + FROM [NestedReferences] AS [n] + LEFT JOIN [NestedReferencesDerived] AS [n0] ON [n].[Id] = [n0].[Id] +) AS [t0] ON [t].[Id] = [t0].[ParentReferenceId] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [b].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [b].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +ORDER BY [b].[Id], [t].[Id], [t0].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + + public override void Nested_include_with_inheritance_reference_reference_reverse() + { + base.Nested_include_with_inheritance_reference_reference_reverse(); + + AssertSql( + @"SELECT [n].[Id], [n].[Name], [n].[ParentCollectionId], [n].[ParentReferenceId], CASE + WHEN [n0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsNestedReferenceDerived], [t].[Id], [t].[BaseParentId], [t].[Name], [t].[IsDerivedReferenceOnBase], [t0].[Id], [t0].[Name], [t0].[BaseId], [t0].[IsDerivedInheritanceRelationshipEntity], [t0].[Id0], [t0].[OwnedReferenceOnBase_Id], [t0].[OwnedReferenceOnBase_Name], [t0].[Id1], [t0].[OwnedReferenceOnDerived_Id], [t0].[OwnedReferenceOnDerived_Name], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [b1].[Name], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id], [d1].[Name] +FROM [NestedReferences] AS [n] +LEFT JOIN [NestedReferencesDerived] AS [n0] ON [n].[Id] = [n0].[Id] +LEFT JOIN ( + SELECT [b].[Id], [b].[BaseParentId], [b].[Name], CASE + WHEN [d].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedReferenceOnBase] + FROM [BaseReferencesOnBase] AS [b] + LEFT JOIN [DerivedReferencesOnBase] AS [d] ON [b].[Id] = [d].[Id] +) AS [t] ON [n].[ParentReferenceId] = [t].[Id] +LEFT JOIN ( + SELECT [b0].[Id], [b0].[Name], [d0].[BaseId], CASE + WHEN [d0].[Id] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsDerivedInheritanceRelationshipEntity], [b0].[Id] AS [Id0], [b0].[OwnedReferenceOnBase_Id], [b0].[OwnedReferenceOnBase_Name], [d0].[Id] AS [Id1], [d0].[OwnedReferenceOnDerived_Id], [d0].[OwnedReferenceOnDerived_Name] + FROM [BaseEntities] AS [b0] + LEFT JOIN [DerivedEntities] AS [d0] ON [b0].[Id] = [d0].[Id] +) AS [t0] ON [t].[BaseParentId] = [t0].[Id] +LEFT JOIN [BaseEntities_OwnedCollectionOnBase] AS [b1] ON [t0].[Id] = [b1].[BaseInheritanceRelationshipEntityId] +LEFT JOIN [DerivedEntities_OwnedCollectionOnDerived] AS [d1] ON [t0].[Id] = [d1].[DerivedInheritanceRelationshipEntityId] +ORDER BY [n].[Id], [t].[Id], [t0].[Id], [b1].[BaseInheritanceRelationshipEntityId], [b1].[Id], [d1].[DerivedInheritanceRelationshipEntityId], [d1].[Id]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs b/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs index c3cf4752b2a..44601b32cf4 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/UdfDbFunctionSqlServerTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.EntityFrameworkCore.TestUtilities; @@ -742,10 +742,11 @@ public override void QF_Correlated_Func_Call_With_Navigation() { base.QF_Correlated_Func_Call_With_Navigation(); - AssertSql(@"SELECT [c].[Id], [t].[LastName], [t].[OrderId], [t].[Id] + AssertSql( + @"SELECT [c].[Id], [t].[CustomerName], [t].[OrderId], [t].[Id] FROM [Customers] AS [c] OUTER APPLY ( - SELECT [c0].[LastName], [m].[OrderId], [c0].[Id] + SELECT [c0].[LastName] AS [CustomerName], [m].[OrderId], [c0].[Id] FROM [dbo].[GetOrdersWithMultipleProducts]([c].[Id]) AS [m] INNER JOIN [Customers] AS [c0] ON [m].[CustomerId] = [c0].[Id] ) AS [t] diff --git a/test/EFCore.SqlServer.FunctionalTests/TPTTableSplittingSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/TPTTableSplittingSqlServerTest.cs new file mode 100644 index 00000000000..9043935dff4 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/TPTTableSplittingSqlServerTest.cs @@ -0,0 +1,293 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.EntityFrameworkCore.TestUtilities; +using Xunit.Abstractions; + +namespace Microsoft.EntityFrameworkCore +{ + public class TPTTableSplittingSqlServerTest : TPTTableSplittingTestBase + { + public TPTTableSplittingSqlServerTest(ITestOutputHelper testOutputHelper) + : base(testOutputHelper) + { + } + + protected override ITestStoreFactory TestStoreFactory => SqlServerTestStoreFactory.Instance; + + public override void Can_use_with_redundant_relationships() + { + base.Can_use_with_redundant_relationships(); + + AssertSql( + @"SELECT [v].[Name], [v].[SeatingCapacity], CASE + WHEN [p].[Name] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsPoweredVehicle], [t0].[Name], [t0].[Operator_Name], [t0].[RequiredInt], [t0].[LicenseType], [t0].[IsLicensedOperator], [t3].[Name], [t3].[Type], [t5].[Name], [t5].[Description], [t5].[IsContinuousCombustionEngine], [t5].[IsIntermittentCombustionEngine], [t5].[IsSolidRocket], [t7].[VehicleName], [t7].[Capacity], [t7].[FuelType], [t7].[GrainGeometry], [t7].[IsSolidFuelTank] +FROM [Vehicles] AS [v] +LEFT JOIN [PoweredVehicles] AS [p] ON [v].[Name] = [p].[Name] +LEFT JOIN ( + SELECT [v0].[Name], [v0].[Operator_Name], [v0].[RequiredInt], [l].[LicenseType], CASE + WHEN [l].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsLicensedOperator], [t].[Name] AS [Name0] + FROM [Vehicles] AS [v0] + LEFT JOIN [LicensedOperators] AS [l] ON [v0].[Name] = [l].[VehicleName] + INNER JOIN ( + SELECT [v1].[Name], [v1].[SeatingCapacity], CASE + WHEN [p0].[Name] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsPoweredVehicle] + FROM [Vehicles] AS [v1] + LEFT JOIN [PoweredVehicles] AS [p0] ON [v1].[Name] = [p0].[Name] + ) AS [t] ON [v0].[Name] = [t].[Name] + WHERE [v0].[RequiredInt] IS NOT NULL +) AS [t0] ON [v].[Name] = [t0].[Name] +LEFT JOIN ( + SELECT [v2].[Name], [v2].[Type], [t2].[Name] AS [Name0], [t2].[Name0] AS [Name00] + FROM [Vehicles] AS [v2] + INNER JOIN ( + SELECT [v3].[Name], [v3].[Operator_Name], [v3].[RequiredInt], [l0].[LicenseType], CASE + WHEN [l0].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsLicensedOperator], [t1].[Name] AS [Name0] + FROM [Vehicles] AS [v3] + LEFT JOIN [LicensedOperators] AS [l0] ON [v3].[Name] = [l0].[VehicleName] + INNER JOIN ( + SELECT [v4].[Name], [v4].[SeatingCapacity], CASE + WHEN [p1].[Name] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsPoweredVehicle] + FROM [Vehicles] AS [v4] + LEFT JOIN [PoweredVehicles] AS [p1] ON [v4].[Name] = [p1].[Name] + ) AS [t1] ON [v3].[Name] = [t1].[Name] + WHERE [v3].[RequiredInt] IS NOT NULL + ) AS [t2] ON [v2].[Name] = [t2].[Name] + WHERE [v2].[Type] IS NOT NULL +) AS [t3] ON [t0].[Name] = [t3].[Name] +LEFT JOIN ( + SELECT [p2].[Name], [p2].[Description], CASE + WHEN [c0].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsContinuousCombustionEngine], CASE + WHEN [i].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsIntermittentCombustionEngine], CASE + WHEN [s].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsSolidRocket], [t4].[Name] AS [Name0] + FROM [PoweredVehicles] AS [p2] + LEFT JOIN [CombustionEngines] AS [c] ON [p2].[Name] = [c].[VehicleName] + LEFT JOIN [ContinuousCombustionEngines] AS [c0] ON [p2].[Name] = [c0].[VehicleName] + LEFT JOIN [IntermittentCombustionEngines] AS [i] ON [p2].[Name] = [i].[VehicleName] + LEFT JOIN [SolidRockets] AS [s] ON [p2].[Name] = [s].[VehicleName] + INNER JOIN ( + SELECT [v5].[Name], [v5].[SeatingCapacity] + FROM [Vehicles] AS [v5] + INNER JOIN [PoweredVehicles] AS [p3] ON [v5].[Name] = [p3].[Name] + ) AS [t4] ON [p2].[Name] = [t4].[Name] + WHERE [p2].[Description] IS NOT NULL +) AS [t5] ON [v].[Name] = [t5].[Name] +LEFT JOIN ( + SELECT [c1].[VehicleName], [c1].[Capacity], [c1].[FuelType], [s0].[GrainGeometry], CASE + WHEN [s0].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsSolidFuelTank], [t6].[Name] + FROM [CombustionEngines] AS [c1] + LEFT JOIN [SolidFuelTanks] AS [s0] ON [c1].[VehicleName] = [s0].[VehicleName] + INNER JOIN ( + SELECT [p4].[Name], [p4].[Description], CASE + WHEN [c3].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsContinuousCombustionEngine], CASE + WHEN [i0].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsIntermittentCombustionEngine], CASE + WHEN [s1].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsSolidRocket] + FROM [PoweredVehicles] AS [p4] + INNER JOIN [CombustionEngines] AS [c2] ON [p4].[Name] = [c2].[VehicleName] + LEFT JOIN [ContinuousCombustionEngines] AS [c3] ON [p4].[Name] = [c3].[VehicleName] + LEFT JOIN [IntermittentCombustionEngines] AS [i0] ON [p4].[Name] = [i0].[VehicleName] + LEFT JOIN [SolidRockets] AS [s1] ON [p4].[Name] = [s1].[VehicleName] + ) AS [t6] ON [c1].[VehicleName] = [t6].[Name] + WHERE [c1].[FuelType] IS NOT NULL OR [c1].[Capacity] IS NOT NULL +) AS [t7] ON [t5].[Name] = [t7].[VehicleName] +ORDER BY [v].[Name]"); + } + + public override void Can_query_shared() + { + base.Can_query_shared(); + + AssertSql( + @"SELECT [v].[Name], [v].[Operator_Name], [v].[RequiredInt], [l].[LicenseType], CASE + WHEN [l].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsLicensedOperator] +FROM [Vehicles] AS [v] +LEFT JOIN [LicensedOperators] AS [l] ON [v].[Name] = [l].[VehicleName] +INNER JOIN ( + SELECT [v0].[Name], [v0].[SeatingCapacity], CASE + WHEN [p].[Name] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsPoweredVehicle] + FROM [Vehicles] AS [v0] + LEFT JOIN [PoweredVehicles] AS [p] ON [v0].[Name] = [p].[Name] +) AS [t] ON [v].[Name] = [t].[Name] +WHERE [v].[RequiredInt] IS NOT NULL"); + } + + public override void Can_query_shared_nonhierarchy() + { + base.Can_query_shared_nonhierarchy(); + + AssertSql( + @"SELECT [v].[Name], [v].[Operator_Name], [v].[RequiredInt] +FROM [Vehicles] AS [v] +INNER JOIN ( + SELECT [v0].[Name], [v0].[SeatingCapacity], CASE + WHEN [p].[Name] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsPoweredVehicle] + FROM [Vehicles] AS [v0] + LEFT JOIN [PoweredVehicles] AS [p] ON [v0].[Name] = [p].[Name] +) AS [t] ON [v].[Name] = [t].[Name] +WHERE [v].[RequiredInt] IS NOT NULL"); + } + + public override void Can_query_shared_nonhierarchy_with_nonshared_dependent() + { + base.Can_query_shared_nonhierarchy_with_nonshared_dependent(); + + AssertSql( + @"SELECT [v].[Name], [v].[Operator_Name], [v].[RequiredInt] +FROM [Vehicles] AS [v] +INNER JOIN ( + SELECT [v0].[Name], [v0].[SeatingCapacity], CASE + WHEN [p].[Name] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsPoweredVehicle] + FROM [Vehicles] AS [v0] + LEFT JOIN [PoweredVehicles] AS [p] ON [v0].[Name] = [p].[Name] +) AS [t] ON [v].[Name] = [t].[Name] +WHERE [v].[RequiredInt] IS NOT NULL"); + } + + public override void Can_query_shared_derived_hierarchy() + { + base.Can_query_shared_derived_hierarchy(); + + AssertSql( + @"SELECT [c].[VehicleName], [c].[Capacity], [c].[FuelType], [s].[GrainGeometry], CASE + WHEN [s].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [IsSolidFuelTank] +FROM [CombustionEngines] AS [c] +LEFT JOIN [SolidFuelTanks] AS [s] ON [c].[VehicleName] = [s].[VehicleName] +INNER JOIN ( + SELECT [p].[Name], [p].[Description], CASE + WHEN [c1].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsContinuousCombustionEngine], CASE + WHEN [i].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsIntermittentCombustionEngine], CASE + WHEN [s0].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsSolidRocket] + FROM [PoweredVehicles] AS [p] + INNER JOIN [CombustionEngines] AS [c0] ON [p].[Name] = [c0].[VehicleName] + LEFT JOIN [ContinuousCombustionEngines] AS [c1] ON [p].[Name] = [c1].[VehicleName] + LEFT JOIN [IntermittentCombustionEngines] AS [i] ON [p].[Name] = [i].[VehicleName] + LEFT JOIN [SolidRockets] AS [s0] ON [p].[Name] = [s0].[VehicleName] +) AS [t] ON [c].[VehicleName] = [t].[Name] +WHERE [c].[FuelType] IS NOT NULL OR [c].[Capacity] IS NOT NULL"); + } + + public override void Can_query_shared_derived_nonhierarchy() + { + base.Can_query_shared_derived_nonhierarchy(); + + AssertSql( + @"SELECT [c].[VehicleName], [c].[Capacity], [c].[FuelType] +FROM [CombustionEngines] AS [c] +INNER JOIN ( + SELECT [p].[Name], [p].[Description], CASE + WHEN [c1].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsContinuousCombustionEngine], CASE + WHEN [i].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsIntermittentCombustionEngine], CASE + WHEN [s].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsSolidRocket] + FROM [PoweredVehicles] AS [p] + INNER JOIN [CombustionEngines] AS [c0] ON [p].[Name] = [c0].[VehicleName] + LEFT JOIN [ContinuousCombustionEngines] AS [c1] ON [p].[Name] = [c1].[VehicleName] + LEFT JOIN [IntermittentCombustionEngines] AS [i] ON [p].[Name] = [i].[VehicleName] + LEFT JOIN [SolidRockets] AS [s] ON [p].[Name] = [s].[VehicleName] +) AS [t] ON [c].[VehicleName] = [t].[Name] +WHERE [c].[FuelType] IS NOT NULL OR [c].[Capacity] IS NOT NULL"); + } + + public override void Can_query_shared_derived_nonhierarchy_all_required() + { + base.Can_query_shared_derived_nonhierarchy_all_required(); + + AssertSql( + @"SELECT [c].[VehicleName], [c].[Capacity], [c].[FuelType] +FROM [CombustionEngines] AS [c] +INNER JOIN ( + SELECT [p].[Name], [p].[Description], CASE + WHEN [c1].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsContinuousCombustionEngine], CASE + WHEN [i].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsIntermittentCombustionEngine], CASE + WHEN [s].[VehicleName] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) + END AS [IsSolidRocket] + FROM [PoweredVehicles] AS [p] + INNER JOIN [CombustionEngines] AS [c0] ON [p].[Name] = [c0].[VehicleName] + LEFT JOIN [ContinuousCombustionEngines] AS [c1] ON [p].[Name] = [c1].[VehicleName] + LEFT JOIN [IntermittentCombustionEngines] AS [i] ON [p].[Name] = [i].[VehicleName] + LEFT JOIN [SolidRockets] AS [s] ON [p].[Name] = [s].[VehicleName] +) AS [t] ON [c].[VehicleName] = [t].[Name] +WHERE [c].[FuelType] IS NOT NULL AND [c].[Capacity] IS NOT NULL"); + } + + public override void Can_change_dependent_instance_non_derived() + { + base.Can_change_dependent_instance_non_derived(); + + AssertSql( + @"@p3='Trek Pro Fit Madone 6 Series' (Nullable = false) (Size = 450) +@p0='LicensedOperator' (Nullable = false) (Size = 4000) +@p1='repairman' (Size = 4000) +@p2='Repair' (Size = 4000) + +SET NOCOUNT ON; +UPDATE [Vehicles] SET [Operator_Discriminator] = @p0, [Operator_Name] = @p1, [LicenseType] = @p2 +WHERE [Name] = @p3; +SELECT @@ROWCOUNT;"); + } + + public override void Can_change_principal_instance_non_derived() + { + base.Can_change_principal_instance_non_derived(); + + AssertSql( + @"@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;"); + } + } +} diff --git a/test/EFCore.Sqlite.FunctionalTests/TPTTableSplittingSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/TPTTableSplittingSqliteTest.cs new file mode 100644 index 00000000000..091a4d1a2b4 --- /dev/null +++ b/test/EFCore.Sqlite.FunctionalTests/TPTTableSplittingSqliteTest.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.EntityFrameworkCore.TestUtilities; +using Xunit.Abstractions; + +namespace Microsoft.EntityFrameworkCore +{ + public class TPTTableSplittingSqliteTest : TPTTableSplittingTestBase + { + public TPTTableSplittingSqliteTest(ITestOutputHelper testOutputHelper) + : base(testOutputHelper) + { + } + + protected override ITestStoreFactory TestStoreFactory => SqliteTestStoreFactory.Instance; + } +}