diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.cs index 37415cb8fb3..224dbf03151 100644 --- a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.cs +++ b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.cs @@ -59,7 +59,7 @@ protected override Expression VisitShapedQuery(ShapedQueryExpression shapedQuery } shaperBody = new JObjectInjectingExpressionVisitor().Visit(shaperBody); - shaperBody = InjectEntityMaterializers(shaperBody); + shaperBody = InjectStructuralTypeMaterializers(shaperBody); if (shapedQueryExpression.QueryExpression is not SelectExpression selectExpression) { diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryCompilingExpressionVisitor.ShaperExpressionProcessingExpressionVisitor.cs b/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryCompilingExpressionVisitor.ShaperExpressionProcessingExpressionVisitor.cs index 59c7cde2fe0..7f435655384 100644 --- a/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryCompilingExpressionVisitor.ShaperExpressionProcessingExpressionVisitor.cs +++ b/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryCompilingExpressionVisitor.ShaperExpressionProcessingExpressionVisitor.cs @@ -76,7 +76,7 @@ protected override Expression VisitExtension(Expression extensionExpression) variable = Parameter(shaper.StructuralType.ClrType); _variables.Add(variable); var innerShaper = - _inMemoryShapedQueryCompilingExpressionVisitor.InjectEntityMaterializers(shaper); + _inMemoryShapedQueryCompilingExpressionVisitor.InjectStructuralTypeMaterializers(shaper); innerShaper = Visit(innerShaper); _expressions.Add(Assign(variable, innerShaper)); _mapping[key] = variable; diff --git a/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs index d0ee126a44c..ca9d17a642f 100644 --- a/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs +++ b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs @@ -131,6 +131,7 @@ public EntityFrameworkRelationalServicesBuilder(IServiceCollection serviceCollec /// This builder, such that further calls can be chained. public override EntityFrameworkServicesBuilder TryAddCoreServices() { + TryAdd(); TryAdd(); TryAdd, ModificationCommandComparer>(); TryAdd(); diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs index b14858e928a..45aea183d1a 100644 --- a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs +++ b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs @@ -624,7 +624,7 @@ private static void CreateContainerColumn( } else { - complexType = ((IComplexType)mappedType); + complexType = (IComplexType)mappedType; #pragma warning disable EF1001 // Internal EF Core API usage. jsonColumn.IsNullable = complexType.ComplexProperty.IsNullable || complexType.ComplexProperty.GetChainToComplexProperty(fromEntity: true).Any(p => p.IsNullable); #pragma warning restore EF1001 // Internal EF Core API usage. diff --git a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs index 92bfc9929e6..5485801c0e6 100644 --- a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs +++ b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs @@ -865,6 +865,14 @@ public static string ExecuteUpdateSubqueryNotSupportedOverComplexTypes(object? c GetString("ExecuteUpdateSubqueryNotSupportedOverComplexTypes", nameof(complexType)), complexType); + /// + /// ExecuteUpdate is being used over type '{structuralType}' which is mapped to JSON; ExecuteUpdate on JSON is not supported. + /// + public static string ExecuteUpdateOverJsonIsNotSupported(object? structuralType) + => string.Format( + GetString("ExecuteUpdateOverJsonIsNotSupported", nameof(structuralType)), + structuralType); + /// /// Can't use explicitly named default constraints with TPC inheritance or entity splitting. Constraint name: '{explicitDefaultConstraintName}'. /// @@ -2192,7 +2200,7 @@ public static string UnsupportedOperatorForSqlExpression(object? nodeType, objec nodeType, expressionType); /// - /// No relational type mapping can be found for property '{entity}.{property}' and the current provider doesn't specify a default store type for the properties of type '{clrType}'. + /// No relational type mapping can be found for property '{entity}.{property}' and the current provider doesn't specify a default store type for the properties of type '{clrType}'. /// public static string UnsupportedPropertyType(object? entity, object? property, object? clrType) => string.Format( diff --git a/src/EFCore.Relational/Properties/RelationalStrings.resx b/src/EFCore.Relational/Properties/RelationalStrings.resx index 3019cfaf63c..5ef534dfacf 100644 --- a/src/EFCore.Relational/Properties/RelationalStrings.resx +++ b/src/EFCore.Relational/Properties/RelationalStrings.resx @@ -442,6 +442,9 @@ ExecuteUpdate is being used over a LINQ operator which isn't natively supported by the database; this cannot be translated because complex type '{complexType}' is projected out. Rewrite your query to project out the containing entity type instead. + + ExecuteUpdate is being used over type '{structuralType}' which is mapped to JSON; ExecuteUpdate on JSON is not supported. + Can't use explicitly named default constraints with TPC inheritance or entity splitting. Constraint name: '{explicitDefaultConstraintName}'. diff --git a/src/EFCore.Relational/Query/CollectionResultExpression.cs b/src/EFCore.Relational/Query/CollectionResultExpression.cs index 99c9ef63ea4..17aaee5be67 100644 --- a/src/EFCore.Relational/Query/CollectionResultExpression.cs +++ b/src/EFCore.Relational/Query/CollectionResultExpression.cs @@ -13,42 +13,33 @@ namespace Microsoft.EntityFrameworkCore.Query; /// not used in application code. /// /// -public class CollectionResultExpression : Expression, IPrintableExpression +/// Represents the server-side query expression for the collection. +/// A navigation associated with this collection, if any. +/// The clr type of individual elements in the collection. +public class CollectionResultExpression( + Expression queryExpression, + IPropertyBase? relationship, + Type elementType) + : Expression, IPrintableExpression { - /// - /// Creates a new instance of the class. - /// - /// An expression representing how to get the subquery from SelectExpression to get the elements. - /// A navigation associated with this collection, if any. - /// The clr type of individual elements in the collection. - public CollectionResultExpression( - ProjectionBindingExpression projectionBindingExpression, - INavigationBase? navigation, - Type elementType) - { - ProjectionBindingExpression = projectionBindingExpression; - Navigation = navigation; - ElementType = elementType; - } - /// /// The expression to get the subquery for this collection. /// - public virtual ProjectionBindingExpression ProjectionBindingExpression { get; } + public virtual Expression QueryExpression { get; } = queryExpression; /// - /// The navigation if associated with the collection. + /// The relationship associated with the collection, if any. /// - public virtual INavigationBase? Navigation { get; } + public virtual IPropertyBase? Relationship { get; } = relationship; /// /// The clr type of elements of the collection. /// - public virtual Type ElementType { get; } + public virtual Type ElementType { get; } = elementType; /// public override Type Type - => ProjectionBindingExpression.Type; + => QueryExpression.Type; /// public override ExpressionType NodeType @@ -56,22 +47,18 @@ public override ExpressionType NodeType /// protected override Expression VisitChildren(ExpressionVisitor visitor) - { - var projectionBindingExpression = (ProjectionBindingExpression)visitor.Visit(ProjectionBindingExpression); - - return Update(projectionBindingExpression); - } + => Update(visitor.Visit(QueryExpression)); /// /// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will /// return this expression. /// - /// The property of the result. + /// The property of the result. /// This expression if no children changed, or an expression with the updated children. - public virtual CollectionResultExpression Update(ProjectionBindingExpression projectionBindingExpression) - => projectionBindingExpression != ProjectionBindingExpression - ? new CollectionResultExpression(projectionBindingExpression, Navigation, ElementType) - : this; + public virtual CollectionResultExpression Update(Expression queryExpression) + => queryExpression == QueryExpression + ? this + : new CollectionResultExpression(queryExpression, Relationship, ElementType); /// public virtual void Print(ExpressionPrinter expressionPrinter) @@ -79,15 +66,28 @@ public virtual void Print(ExpressionPrinter expressionPrinter) expressionPrinter.AppendLine("CollectionResultExpression:"); using (expressionPrinter.Indent()) { - expressionPrinter.Append("ProjectionBindingExpression:"); - expressionPrinter.Visit(ProjectionBindingExpression); + expressionPrinter.Append("QueryExpression:"); + expressionPrinter.Visit(QueryExpression); expressionPrinter.AppendLine(); - if (Navigation != null) + + if (Relationship is not null) { - expressionPrinter.Append("Navigation:").AppendLine(Navigation.ToString()!); + expressionPrinter.Append("Relationship:").AppendLine(Relationship.ToString()!); } expressionPrinter.Append("ElementType:").AppendLine(ElementType.ShortDisplayName()); } } + + /// + /// The expression to get the subquery for this collection. + /// + [Obsolete("Use QueryExpression instead.", error: true)] + public virtual ProjectionBindingExpression ProjectionBindingExpression { get; } = null!; + + /// + /// The navigation if associated with the collection. + /// + [Obsolete("Use Relationship instead.", error: true)] + public virtual INavigationBase? Navigation { get; } } diff --git a/src/EFCore.Relational/Query/Internal/QueryableJsonProjectionInfo.cs b/src/EFCore.Relational/Query/Internal/QueryableJsonProjectionInfo.cs index 12b168b43c3..7ca6c3797c4 100644 --- a/src/EFCore.Relational/Query/Internal/QueryableJsonProjectionInfo.cs +++ b/src/EFCore.Relational/Query/Internal/QueryableJsonProjectionInfo.cs @@ -18,7 +18,7 @@ public readonly struct QueryableJsonProjectionInfo /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public QueryableJsonProjectionInfo( - Dictionary propertyIndexMap, + Dictionary propertyIndexMap, List<(JsonProjectionInfo, INavigation)> childrenProjectionInfo) { PropertyIndexMap = propertyIndexMap; @@ -34,7 +34,7 @@ public QueryableJsonProjectionInfo( /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public IDictionary PropertyIndexMap { get; } + public IDictionary PropertyIndexMap { get; } /// /// Information needed to construct each child JSON entity. diff --git a/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs b/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs index 937410dd762..610c974c741 100644 --- a/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs @@ -198,7 +198,7 @@ public virtual Expression Translate(SelectExpression selectExpression, Expressio // expression.Type here will be List return new CollectionResultExpression( new ProjectionBindingExpression(_selectExpression, _clientProjections.Count - 1, expression.Type), - navigation: null, + relationship: null, methodCallExpression.Method.GetGenericArguments()[0]); } } @@ -219,7 +219,7 @@ public virtual Expression Translate(SelectExpression selectExpression, Expressio _selectExpression, _clientProjections.Count - 1, type); return subquery.ResultCardinality == ResultCardinality.Enumerable ? new CollectionResultExpression( - projectionBindingExpression, navigation: null, subquery.ShaperExpression.Type) + projectionBindingExpression, relationship: null, subquery.ShaperExpression.Type) : projectionBindingExpression; } } @@ -242,6 +242,9 @@ public virtual Expression Translate(SelectExpression selectExpression, Expressio case RelationalStructuralTypeShaperExpression { StructuralType: IComplexType } shaper: return base.Visit(shaper); + case CollectionResultExpression collectionResult: + return base.Visit(collectionResult); + case null or RelationalStructuralTypeShaperExpression { StructuralType: IEntityType }: return QueryCompilationContext.NotTranslatedExpression; @@ -386,16 +389,16 @@ protected override Expression VisitExtension(Expression extensionExpression) return QueryCompilationContext.NotTranslatedExpression; } - case CollectionResultExpression collectionResultExpression: + case CollectionResultExpression { QueryExpression: ProjectionBindingExpression projectionBindingExpression } collectionResultExpression: { - // TODO this should not be needed at some point, we shouldn't be revisit same projection. + // TODO this should not be needed at some point, we shouldn't be revisiting same projection. // This happens because we don't process result selector for Join/SelectMany directly. if (_indexBasedBinding) { Check.DebugAssert( - ReferenceEquals(_selectExpression, collectionResultExpression.ProjectionBindingExpression.QueryExpression), + ReferenceEquals(_selectExpression, projectionBindingExpression.QueryExpression), "The projection should belong to same select expression."); - var mappedProjection = _selectExpression.GetProjection(collectionResultExpression.ProjectionBindingExpression); + var mappedProjection = _selectExpression.GetProjection(projectionBindingExpression); _clientProjections!.Add(mappedProjection); return collectionResultExpression.Update( @@ -406,6 +409,21 @@ protected override Expression VisitExtension(Expression extensionExpression) return QueryCompilationContext.NotTranslatedExpression; } + case CollectionResultExpression { QueryExpression: JsonQueryExpression jsonQuery } collectionResult: + { + if (_indexBasedBinding) + { + _clientProjections!.Add(jsonQuery); + } + else + { + _projectionMapping[_projectionMembers.Peek()] = jsonQuery; + } + + return collectionResult.Update( + new ProjectionBindingExpression(_selectExpression, _projectionMembers.Peek(), collectionResult.Type)); + } + default: throw new InvalidOperationException(CoreStrings.TranslationFailed(extensionExpression.Print())); } @@ -633,7 +651,9 @@ private static Expression MatchTypes(Expression expression, Type targetType) if (targetType != expression.Type && targetType.TryGetElementType(typeof(IQueryable<>)) == null) { - Check.DebugAssert(targetType.MakeNullable() == expression.Type, "expression.Type must be nullable of targetType"); + Check.DebugAssert( + targetType.MakeNullable() == expression.Type, + $"expression has type {expression.Type.Name}, but must be nullable over {targetType.Name}"); expression = Expression.Convert(expression, targetType); } diff --git a/src/EFCore.Relational/Query/Internal/RelationalStructuralTypeMaterializerSource.cs b/src/EFCore.Relational/Query/Internal/RelationalStructuralTypeMaterializerSource.cs new file mode 100644 index 00000000000..43400fe11c1 --- /dev/null +++ b/src/EFCore.Relational/Query/Internal/RelationalStructuralTypeMaterializerSource.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Internal; + +#pragma warning disable EF1001 // EntityMaterializerSource is pubternal + +/// +/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to +/// the same compatibility standards as public APIs. It may be changed or removed without notice in +/// any release. You should only use it directly in your code with extreme caution and knowing that +/// doing so can result in application failures when updating to a new Entity Framework Core release. +/// +public class RelationalStructuralTypeMaterializerSource(StructuralTypeMaterializerSourceDependencies dependencies) + : StructuralTypeMaterializerSource(dependencies) +{ + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + protected override void AddInitializeExpression( + IPropertyBase property, + ParameterBindingInfo bindingInfo, + Expression instanceVariable, + MethodCallExpression valueBufferExpression, + List blockExpressions) + { + // JSON complex properties are not handled in the initial materialization expression, since they're not + // simply e.g. DbDataReader.GetFieldValue<>() calls. So they're handled afterwards in the shaper, and need + // to be skipped here. + if (property is IComplexProperty { ComplexType: var complexType } && complexType.IsMappedToJson()) + { + return; + } + + base.AddInitializeExpression(property, bindingInfo, instanceVariable, valueBufferExpression, blockExpressions); + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [EntityFrameworkInternal] + public static readonly MethodInfo MaterializeJsonComplexTypeMethod + = typeof(RelationalStructuralTypeMaterializerSource).GetTypeInfo().GetDeclaredMethod(nameof(MaterializeJsonComplexType))!; + + private static T MaterializeJsonComplexType(in ValueBuffer valueBuffer, IComplexProperty complexProperty) + => throw new UnreachableException(); +} diff --git a/src/EFCore.Relational/Query/JsonQueryExpression.cs b/src/EFCore.Relational/Query/JsonQueryExpression.cs index 92b2440bef6..72144dfc179 100644 --- a/src/EFCore.Relational/Query/JsonQueryExpression.cs +++ b/src/EFCore.Relational/Query/JsonQueryExpression.cs @@ -19,19 +19,19 @@ public class JsonQueryExpression : Expression, IPrintableExpression /// /// Creates a new instance of the class. /// - /// An entity type being represented by this expression. - /// A column containing JSON value. - /// A map of key properties and columns they map to in the database. - /// A type of the element represented by this expression. - /// A value indicating whether this expression represents a collection or not. + /// The structural type represented by this expression. + /// A column containing the JSON value. + /// For owned entities, a map of key properties and columns they map to in the database. For complex types, . + /// The CLR represented by this expression. + /// Whether this expression represents a collection. public JsonQueryExpression( - IEntityType entityType, + ITypeBase structuralType, ColumnExpression jsonColumn, - IReadOnlyDictionary keyPropertyMap, + IReadOnlyDictionary? keyPropertyMap, Type type, bool collection) : this( - entityType, + structuralType, jsonColumn, keyPropertyMap, path: [], @@ -41,18 +41,28 @@ public JsonQueryExpression( { } - private JsonQueryExpression( - IEntityType entityType, + /// + /// Creates a new instance of the class. + /// + /// The structural type represented by this expression. + /// A column containing the JSON value. + /// For owned entities, a map of key properties and columns they map to in the database. For complex types, . + /// The list of path segments leading to the entity from the root of the JSON stored in the column. + /// The CLR represented by this expression. + /// Whether this expression represents a collection. + /// Whether this expression is nullable. + public JsonQueryExpression( + ITypeBase structuralType, ColumnExpression jsonColumn, - IReadOnlyDictionary keyPropertyMap, + IReadOnlyDictionary? keyPropertyMap, IReadOnlyList path, Type type, bool collection, bool nullable) { - Check.DebugAssert(entityType.FindPrimaryKey() != null, "primary key is null."); + Check.DebugAssert(structuralType is not IEntityType entityType || entityType.FindPrimaryKey() is not null, "JsonQueryExpression over keyless entity type"); - EntityType = entityType; + StructuralType = structuralType; JsonColumn = jsonColumn; IsCollection = collection; KeyPropertyMap = keyPropertyMap; @@ -62,17 +72,17 @@ private JsonQueryExpression( } /// - /// The entity type being represented by this expression. + /// The structural type represented by this expression. /// - public virtual IEntityType EntityType { get; } + public virtual ITypeBase StructuralType { get; } /// - /// The column containing JSON value. + /// The column containing the JSON value. /// public virtual ColumnExpression JsonColumn { get; } /// - /// The value indicating whether this expression represents a collection. + /// Whether this expression represents a collection. /// public virtual bool IsCollection { get; } @@ -82,7 +92,7 @@ private JsonQueryExpression( public virtual IReadOnlyList Path { get; } /// - /// The value indicating whether this expression is nullable. + /// Whether this expression is nullable. /// public virtual bool IsNullable { get; } @@ -93,7 +103,7 @@ private JsonQueryExpression( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - public virtual IReadOnlyDictionary KeyPropertyMap { get; } + public virtual IReadOnlyDictionary? KeyPropertyMap { get; } /// public override ExpressionType NodeType @@ -107,66 +117,102 @@ public override ExpressionType NodeType /// public virtual SqlExpression BindProperty(IProperty property) { - if (!EntityType.IsAssignableFrom(property.DeclaringType) - && !property.DeclaringType.IsAssignableFrom(EntityType)) + if (!StructuralType.IsAssignableFrom(property.DeclaringType) + && !property.DeclaringType.IsAssignableFrom(StructuralType)) { throw new InvalidOperationException( - RelationalStrings.UnableToBindMemberToEntityProjection("property", property.Name, EntityType.DisplayName())); + RelationalStrings.UnableToBindMemberToEntityProjection("property", property.Name, StructuralType.DisplayName())); } - if (KeyPropertyMap.TryGetValue(property, out var match)) + if (KeyPropertyMap?.TryGetValue(property, out var match) == true) { return match; } - var newPath = Path.ToList(); - newPath.Add(new PathSegment(property.GetJsonPropertyName()!)); - return new JsonScalarExpression( JsonColumn, - newPath, + [.. Path, new(property.GetJsonPropertyName()!)], property.ClrType.UnwrapNullableType(), property.FindRelationalTypeMapping()!, IsNullable || property.IsNullable); } /// - /// Binds a navigation with this JSON query expression to get the SQL representation. + /// Binds a relationship with this JSON query expression to get the SQL representation. /// - /// The navigation to bind. - /// An JSON query expression for the target entity type of the navigation. - public virtual JsonQueryExpression BindNavigation(INavigation navigation) + /// The navigation or complex property to bind. + /// An JSON query expression for the target entity or complex type. + public virtual JsonQueryExpression BindRelationship(IPropertyBase relationship) { - if (navigation.ForeignKey.DependentToPrincipal == navigation) + switch (relationship) { - // issue #28645 - throw new InvalidOperationException( - RelationalStrings.JsonCantNavigateToParentEntity( - navigation.ForeignKey.DeclaringEntityType.DisplayName(), - navigation.ForeignKey.PrincipalEntityType.DisplayName(), - navigation.Name)); - } + case INavigation navigation: + { + if (StructuralType is not IEntityType entityType) + { + throw new UnreachableException("Navigation on complex JSON type"); + } + + Check.DebugAssert(KeyPropertyMap is not null); + + if (navigation.ForeignKey.DependentToPrincipal == navigation) + { + // issue #28645 + throw new InvalidOperationException( + RelationalStrings.JsonCantNavigateToParentEntity( + navigation.ForeignKey.DeclaringEntityType.DisplayName(), + navigation.ForeignKey.PrincipalEntityType.DisplayName(), + navigation.Name)); + } + + var targetEntityType = navigation.TargetEntityType; + var newPath = Path.ToList(); + newPath.Add(new PathSegment(targetEntityType.GetJsonPropertyName()!)); + + var newKeyPropertyMap = new Dictionary(); + var targetPrimaryKeyProperties = targetEntityType.FindPrimaryKey()!.Properties.Take(KeyPropertyMap.Count); + var sourcePrimaryKeyProperties = entityType.FindPrimaryKey()!.Properties.Take(KeyPropertyMap.Count); + foreach (var (target, source) in targetPrimaryKeyProperties.Zip(sourcePrimaryKeyProperties, (t, s) => (t, s))) + { + newKeyPropertyMap[target] = KeyPropertyMap[source]; + } + + return new JsonQueryExpression( + targetEntityType, + JsonColumn, + newKeyPropertyMap, + newPath, + navigation.ClrType, + navigation.IsCollection, + IsNullable || !navigation.ForeignKey.IsRequiredDependent); + } - var targetEntityType = navigation.TargetEntityType; - var newPath = Path.ToList(); - newPath.Add(new PathSegment(targetEntityType.GetJsonPropertyName()!)); + case IComplexProperty complexProperty: + { + if (StructuralType is not IComplexType complexType) + { + throw new UnreachableException("Navigation on complex JSON type"); + } + + Check.DebugAssert(KeyPropertyMap is null); + + var targetComplexType = complexProperty.ComplexType; + var newPath = Path.ToList(); + newPath.Add(new PathSegment(targetComplexType.GetJsonPropertyName()!)); + + return new JsonQueryExpression( + targetComplexType, + JsonColumn, + keyPropertyMap: null, + newPath, + complexProperty.ClrType, + complexProperty.IsCollection, + IsNullable || complexProperty.IsNullable); + } - var newKeyPropertyMap = new Dictionary(); - var targetPrimaryKeyProperties = targetEntityType.FindPrimaryKey()!.Properties.Take(KeyPropertyMap.Count); - var sourcePrimaryKeyProperties = EntityType.FindPrimaryKey()!.Properties.Take(KeyPropertyMap.Count); - foreach (var (target, source) in targetPrimaryKeyProperties.Zip(sourcePrimaryKeyProperties, (t, s) => (t, s))) - { - newKeyPropertyMap[target] = KeyPropertyMap[source]; + default: + throw new UnreachableException(); } - - return new JsonQueryExpression( - targetEntityType, - JsonColumn, - newKeyPropertyMap, - newPath, - navigation.ClrType, - navigation.IsCollection, - IsNullable || !navigation.ForeignKey.IsRequiredDependent); } /// @@ -183,11 +229,11 @@ public virtual JsonQueryExpression BindCollectionElement(SqlExpression collectio newPath.Add(new PathSegment(collectionIndexExpression)); return new JsonQueryExpression( - EntityType, + StructuralType, JsonColumn, KeyPropertyMap, newPath, - EntityType.ClrType, + StructuralType.ClrType, collection: false, // TODO: computing nullability might be more complicated when we allow strict mode // see issue #28656 @@ -199,22 +245,14 @@ public virtual JsonQueryExpression BindCollectionElement(SqlExpression collectio /// /// A new expression which has property set to true. public virtual JsonQueryExpression MakeNullable() - { - var keyPropertyMap = new Dictionary(); - foreach (var (property, columnExpression) in KeyPropertyMap) - { - keyPropertyMap[property] = columnExpression.MakeNullable(); - } - - return new JsonQueryExpression( - EntityType, + => new JsonQueryExpression( + StructuralType, JsonColumn.MakeNullable(), - keyPropertyMap, + KeyPropertyMap?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.MakeNullable()), Path, Type, IsCollection, nullable: true); - } /// public virtual void Print(ExpressionPrinter expressionPrinter) @@ -229,6 +267,12 @@ public virtual void Print(ExpressionPrinter expressionPrinter) protected override Expression VisitChildren(ExpressionVisitor visitor) { var jsonColumn = (ColumnExpression)visitor.Visit(JsonColumn); + + if (KeyPropertyMap is null) + { + return Update(jsonColumn, keyPropertyMap: null); + } + var newKeyPropertyMap = new Dictionary(); foreach (var (property, column) in KeyPropertyMap) { @@ -247,12 +291,15 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) /// This expression if no children changed, or an expression with the updated children. public virtual JsonQueryExpression Update( ColumnExpression jsonColumn, - IReadOnlyDictionary keyPropertyMap) - => jsonColumn != JsonColumn - || keyPropertyMap.Count != KeyPropertyMap.Count - || keyPropertyMap.Zip(KeyPropertyMap, (n, o) => n.Value != o.Value).Any(x => x) - ? new JsonQueryExpression(EntityType, jsonColumn, keyPropertyMap, Path, Type, IsCollection, IsNullable) - : this; + IReadOnlyDictionary? keyPropertyMap) + => (jsonColumn == JsonColumn + && ((keyPropertyMap is null && KeyPropertyMap is null) + || (keyPropertyMap is not null + && KeyPropertyMap is not null + && keyPropertyMap.Count == KeyPropertyMap.Count + && KeyPropertyMapEquals(keyPropertyMap)))) + ? this + : new JsonQueryExpression(StructuralType, jsonColumn, keyPropertyMap, Path, Type, IsCollection, IsNullable); /// public override bool Equals(object? obj) @@ -262,16 +309,21 @@ public override bool Equals(object? obj) && Equals(jsonQueryExpression)); private bool Equals(JsonQueryExpression jsonQueryExpression) - => EntityType.Equals(jsonQueryExpression.EntityType) + => StructuralType.Equals(jsonQueryExpression.StructuralType) && JsonColumn.Equals(jsonQueryExpression.JsonColumn) && IsCollection.Equals(jsonQueryExpression.IsCollection) && IsNullable == jsonQueryExpression.IsNullable && Path.SequenceEqual(jsonQueryExpression.Path) && KeyPropertyMapEquals(jsonQueryExpression.KeyPropertyMap); - private bool KeyPropertyMapEquals(IReadOnlyDictionary other) + private bool KeyPropertyMapEquals(IReadOnlyDictionary? other) { - if (KeyPropertyMap.Count != other.Count) + if (KeyPropertyMap is null && other is null) + { + return true; + } + + if (KeyPropertyMap is null || other is null || KeyPropertyMap.Count != other.Count) { return false; } @@ -290,5 +342,5 @@ private bool KeyPropertyMapEquals(IReadOnlyDictionary public override int GetHashCode() // not incorporating _keyPropertyMap into the hash, too much work - => HashCode.Combine(EntityType, JsonColumn, IsCollection, Path, IsNullable); + => HashCode.Combine(StructuralType, JsonColumn, IsCollection, Path, IsNullable); } diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.CreateSelect.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.CreateSelect.cs index a3676bc23ff..8dd443fe376 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.CreateSelect.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.CreateSelect.cs @@ -586,19 +586,19 @@ protected virtual SelectExpression CreateSelect( throw new ArgumentException(RelationalStrings.SelectCanOnlyBeBuiltOnCollectionJsonQuery, nameof(jsonQueryExpression)); } - var entityType = jsonQueryExpression.EntityType; + var structuralType = jsonQueryExpression.StructuralType; Check.DebugAssert( - entityType.BaseType is null && !entityType.GetDirectlyDerivedTypes().Any(), + structuralType.BaseType is null && !structuralType.GetDirectlyDerivedTypes().Any(), "Inheritance encountered inside a JSON document"); // Create a dictionary mapping all properties to their ColumnExpressions, for the SelectExpression's projection. var propertyExpressions = new Dictionary(); - foreach (var property in entityType.GetPropertiesInHierarchy()) + foreach (var property in structuralType.GetPropertiesInHierarchy()) { - // also adding column(s) representing key of the parent (non-JSON) entity, on top of all the projections from OPENJSON/json_each/etc. - if (jsonQueryExpression.KeyPropertyMap.TryGetValue(property, out var ownerKeyColumn)) + // For owned JSON mapping, add column(s) representing key of the parent (non-JSON) entity, on top of all the projections from OPENJSON/json_each/etc. + if (jsonQueryExpression.KeyPropertyMap?.TryGetValue(property, out var ownerKeyColumn) == true) { propertyExpressions[property] = ownerKeyColumn; continue; @@ -610,67 +610,68 @@ protected virtual SelectExpression CreateSelect( { propertyExpressions[property] = CreateColumnExpression( tableExpressionBase, jsonPropertyName, property.ClrType, property.GetRelationalTypeMapping(), - /*jsonQueryExpression.IsNullable || */property.IsNullable); + /* jsonQueryExpression.IsNullable || */ property.IsNullable); // TODO: } } - var table = entityType.GetViewOrTableMappings().SingleOrDefault()?.Table ?? entityType.GetDefaultMappings().Single().Table; + var table = structuralType.GetViewOrTableMappings().SingleOrDefault()?.Table ?? structuralType.GetDefaultMappings().Single().Table; var tableAlias = tableExpressionBase.Alias!; - - // TODO: We'll need to make sure this is correct when we add support for JSON complex types, #31252 var tableMap = new Dictionary { [table] = tableAlias }; - var projection = new StructuralTypeProjectionExpression( - entityType, - propertyExpressions, - tableMap); + var projection = new StructuralTypeProjectionExpression(structuralType, propertyExpressions, tableMap); - var containerColumnName = entityType.GetContainerColumnName()!; - var containerColumn = table.FindColumn(containerColumnName)!; - var containerColumnTypeMapping = containerColumn.StoreTypeMapping; - foreach (var ownedJsonNavigation in entityType.GetNavigationsInHierarchy() - .Where( - n => n.ForeignKey.IsOwnership - && n.TargetEntityType.IsMappedToJson() - && n.ForeignKey.PrincipalToDependent == n)) + // Go over all owned JSON navigations and pre-populate bindings for them - these get used later if the LINQ query binds to them. + // Note that we don't need to do the same for complex properties, are these are managed lazily: when a complex property is bound, + // we generate everything we need at that point. + if (structuralType is IEntityType entityType) { - var targetEntityType = ownedJsonNavigation.TargetEntityType; - var jsonNavigationName = ownedJsonNavigation.TargetEntityType.GetJsonPropertyName(); - Check.DebugAssert(jsonNavigationName is not null, "Invalid navigation found on JSON-mapped entity"); - var isNullable = containerColumn.IsNullable - || !ownedJsonNavigation.ForeignKey.IsRequiredDependent - || ownedJsonNavigation.IsCollection; - - // The TableExpressionBase represents a relational expansion of the JSON collection. We now need a ColumnExpression to represent - // the specific JSON property (projected as a relational column) which holds the JSON subtree for the target entity. - var column = new ColumnExpression( - jsonNavigationName, - tableAlias, - containerColumnTypeMapping.ClrType, - containerColumnTypeMapping, - isNullable); - - // need to remap key property map to use target entity key properties - var newKeyPropertyMap = new Dictionary(); - var targetPrimaryKeyProperties = targetEntityType.FindPrimaryKey()!.Properties.Take(jsonQueryExpression.KeyPropertyMap.Count); - var sourcePrimaryKeyProperties = - jsonQueryExpression.EntityType.FindPrimaryKey()!.Properties.Take(jsonQueryExpression.KeyPropertyMap.Count); - foreach (var (target, source) in targetPrimaryKeyProperties.Zip(sourcePrimaryKeyProperties, (t, s) => (t, s))) + var containerColumnName = structuralType.GetContainerColumnName()!; + var containerColumn = table.FindColumn(containerColumnName)!; + var containerColumnTypeMapping = containerColumn.StoreTypeMapping; + foreach (var ownedJsonNavigation in entityType.GetNavigationsInHierarchy() + .Where( + n => n.ForeignKey.IsOwnership + && n.TargetEntityType.IsMappedToJson() + && n.ForeignKey.PrincipalToDependent == n)) { - newKeyPropertyMap[target] = jsonQueryExpression.KeyPropertyMap[source]; - } + var targetEntityType = ownedJsonNavigation.TargetEntityType; + var jsonNavigationName = ownedJsonNavigation.TargetEntityType.GetJsonPropertyName(); + Check.DebugAssert(jsonNavigationName is not null, "Invalid navigation found on JSON-mapped entity"); + var isNullable = containerColumn.IsNullable + || !ownedJsonNavigation.ForeignKey.IsRequiredDependent + || ownedJsonNavigation.IsCollection; + + // The TableExpressionBase represents a relational expansion of the JSON collection. We now need a ColumnExpression to represent + // the specific JSON property (projected as a relational column) which holds the JSON subtree for the target entity. + var column = new ColumnExpression( + jsonNavigationName, + tableAlias, + containerColumnTypeMapping.ClrType, + containerColumnTypeMapping, + isNullable); + + // need to remap key property map to use target entity key properties + var newKeyPropertyMap = new Dictionary(); + var targetPrimaryKeyProperties = targetEntityType.FindPrimaryKey()!.Properties.Take(jsonQueryExpression.KeyPropertyMap!.Count); + var sourcePrimaryKeyProperties = + entityType.FindPrimaryKey()!.Properties.Take(jsonQueryExpression.KeyPropertyMap.Count); + foreach (var (target, source) in targetPrimaryKeyProperties.Zip(sourcePrimaryKeyProperties, (t, s) => (t, s))) + { + newKeyPropertyMap[target] = jsonQueryExpression.KeyPropertyMap[source]; + } - var entityShaperExpression = new RelationalStructuralTypeShaperExpression( - targetEntityType, - new JsonQueryExpression( + var entityShaperExpression = new RelationalStructuralTypeShaperExpression( targetEntityType, - column, - newKeyPropertyMap, - ownedJsonNavigation.ClrType, - ownedJsonNavigation.IsCollection), - isNullable); - - projection.AddNavigationBinding(ownedJsonNavigation, entityShaperExpression); + new JsonQueryExpression( + targetEntityType, + column, + newKeyPropertyMap, + ownedJsonNavigation.ClrType, + ownedJsonNavigation.IsCollection), + isNullable); + + projection.AddNavigationBinding(ownedJsonNavigation, entityShaperExpression); + } } var identifierColumn = new ColumnExpression( diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs index 308cf9489b1..456d7a9dde6 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs @@ -156,7 +156,7 @@ bool TranslateSetters( break; } - // TODO: This is for column flattening; implement JSON complex type support as well. + // TODO: This is for column flattening; implement JSON complex type support as well (#28766) case StructuralTypeShaperExpression { StructuralType: IComplexType complexType, @@ -167,6 +167,11 @@ bool TranslateSetters( propertyBase is IComplexProperty complexProperty && complexProperty.ComplexType == complexType, "PropertyBase should be a complex property referring to the correct complex type"); + if (complexType.IsMappedToJson()) + { + throw new InvalidOperationException(RelationalStrings.ExecuteUpdateOverJsonIsNotSupported(complexType.DisplayName())); + } + if (TranslateSetterValueSelector(source, valueSelector, shaper.Type) is not Expression translatedValueSelector || !TryProcessComplexType(shaper, translatedValueSelector)) { @@ -208,6 +213,9 @@ bool IsColumnOnSameTable(ColumnExpression column, LambdaExpression propertySelec return true; } + // Recursively processes the complex types and all complex types referenced by it, adding setters fo all (non-complex) + // properties. + // Note that this only supports table splitting (where all columns are flattened to the table), but not JSON complex types (#28766). bool TryProcessComplexType(StructuralTypeShaperExpression shaperExpression, Expression valueExpression) { if (shaperExpression.StructuralType is not IComplexType complexType @@ -241,7 +249,12 @@ bool TryProcessComplexType(StructuralTypeShaperExpression shaperExpression, Expr // duplicated for every property on the complex type. // TODO: Make this work by using a common table expression (CTE) - var nestedShaperExpression = projection.BindComplexProperty(complexProperty); + if (complexProperty.ComplexType.IsMappedToJson()) + { + throw new InvalidOperationException(RelationalStrings.ExecuteUpdateOverJsonIsNotSupported(complexProperty.ComplexType.DisplayName())); + } + + var nestedShaperExpression = (StructuralTypeShaperExpression)projection.BindComplexProperty(complexProperty); var nestedValueExpression = CreateComplexPropertyAccessExpression(valueExpression, complexProperty); if (!TryProcessComplexType(nestedShaperExpression, nestedValueExpression)) { diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index 18bb222f2f1..48f63e6c20c 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -262,15 +262,31 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp /// protected override ShapedQueryExpression? TranslateMemberAccess(Expression source, MemberIdentity member) { - // Attempt to translate access into a primitive collection property (i.e. array column) - if (_sqlTranslator.TryBindMember(_sqlTranslator.Visit(source), member, out var translatedExpression, out var property) - && property is IProperty { IsPrimitiveCollection: true } regularProperty - && translatedExpression is SqlExpression sqlExpression - && TranslatePrimitiveCollection( - sqlExpression, regularProperty, _sqlAliasManager.GenerateTableAlias(GenerateTableAlias(sqlExpression))) is - { } primitiveCollectionTranslation) + // Attempt to translate access into a primitive or complex collection property (i.e. array column) + if (_sqlTranslator.TryBindMember(_sqlTranslator.Visit(source), member, out var translatedExpression, out var property)) { - return primitiveCollectionTranslation; + switch (property) + { + case IProperty { IsPrimitiveCollection: true } scalarProperty + when translatedExpression is SqlExpression sqlExpression + && TranslatePrimitiveCollection( + sqlExpression, + scalarProperty, + _sqlAliasManager.GenerateTableAlias(GenerateTableAlias(sqlExpression))) is { } primitiveCollectionTranslation: + { + return primitiveCollectionTranslation; + } + + case IComplexProperty { IsCollection: true, ComplexType: var complexType } complexProperty: + Check.DebugAssert(complexType.IsMappedToJson()); + + if (translatedExpression is not CollectionResultExpression { QueryExpression: JsonQueryExpression jsonQuery }) + { + throw new UnreachableException(); + } + + return TransformJsonQueryToTable(jsonQuery); + } } return null; @@ -1605,7 +1621,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp var newJsonQuery = jsonQueryExpression.BindCollectionElement(collectionIndexExpression); var entityShaper = new RelationalStructuralTypeShaperExpression( - jsonQueryExpression.EntityType, + jsonQueryExpression.StructuralType, newJsonQuery, nullable: true); @@ -1781,7 +1797,7 @@ Expression ExpandOwnedNavigation(INavigation navigation) if (TryGetJsonQueryExpression(shaper, out var jsonQueryExpression)) { - var newJsonQueryExpression = jsonQueryExpression.BindNavigation(navigation); + var newJsonQueryExpression = jsonQueryExpression.BindRelationship(navigation); Debug.Assert(!navigation.IsOnDependent, "JSON navigations should always be from principal do dependent"); diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs index 419e8e7bbea..f795d137eb1 100644 --- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs +++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ClientMethods.cs @@ -962,10 +962,10 @@ static async Task InitializeReaderAsync( [EntityFrameworkInternal] public static TEntity? MaterializeJsonEntity( QueryContext queryContext, - object[] keyPropertyValues, + object[]? keyPropertyValues, JsonReaderData? jsonReaderData, bool nullable, - Func shaper) + Func shaper) where TEntity : class { if (jsonReaderData == null) @@ -1007,10 +1007,10 @@ static async Task InitializeReaderAsync( [EntityFrameworkInternal] public static TResult? MaterializeJsonEntityCollection( QueryContext queryContext, - object[] keyPropertyValues, + object[]? keyPropertyValues, JsonReaderData? jsonReaderData, - INavigationBase navigation, - Func innerShaper) + IPropertyBase relationship, + Func innerShaper) where TEntity : class { if (jsonReaderData == null) @@ -1033,18 +1033,23 @@ static async Task InitializeReaderAsync( break; } - var collectionAccessor = navigation.GetCollectionAccessor(); + var collectionAccessor = relationship.GetCollectionAccessor(); var result = (TResult)collectionAccessor!.Create(); - var newKeyPropertyValues = new object[keyPropertyValues.Length + 1]; - Array.Copy(keyPropertyValues, newKeyPropertyValues, keyPropertyValues.Length); + object[]? newKeyPropertyValues = null; + + if (keyPropertyValues is not null) + { + newKeyPropertyValues = new object[keyPropertyValues.Length + 1]; + Array.Copy(keyPropertyValues, newKeyPropertyValues, keyPropertyValues.Length); + } tokenType = manager.MoveNext(); var i = 0; while (tokenType != JsonTokenType.EndArray) { - newKeyPropertyValues[^1] = ++i; + newKeyPropertyValues?[^1] = ++i; if (tokenType == JsonTokenType.StartObject) { @@ -1082,10 +1087,10 @@ static async Task InitializeReaderAsync( [EntityFrameworkInternal] public static void IncludeJsonEntityReference( QueryContext queryContext, - object[] keyPropertyValues, + object[]? keyPropertyValues, JsonReaderData? jsonReaderData, TIncludingEntity entity, - Func innerShaper, + Func innerShaper, Action fixup, bool trackingQuery) where TIncludingEntity : class @@ -1126,10 +1131,10 @@ public static void IncludeJsonEntityReference [EntityFrameworkInternal] public static void IncludeJsonEntityCollection( QueryContext queryContext, - object[] keyPropertyValues, + object[]? keyPropertyValues, JsonReaderData? jsonReaderData, TIncludingEntity entity, - Func innerShaper, + Func innerShaper, Action getOrCreateCollectionObject, Action fixup, bool trackingQuery) @@ -1156,15 +1161,20 @@ public static void IncludeJsonEntityCollection /// States to convert code to data reader read /// - private readonly Dictionary> _materializationContextBindings = new(); + private readonly Dictionary> _materializationContextBindings = new(); private readonly Dictionary _entityTypeIdentifyingExpressionInfo = new(); private readonly Dictionary _singleEntityTypeDiscriminatorValues = new(); @@ -490,10 +490,12 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression) if (newExpression.Arguments[0] is ProjectionBindingExpression projectionBindingExpression) { - var projectionIndex = GetProjectionIndex(projectionBindingExpression); - var propertyMap = projectionIndex is IDictionary - ? (IDictionary)projectionIndex - : ((QueryableJsonProjectionInfo)projectionIndex).PropertyIndexMap; + var propertyMap = GetProjectionIndex(projectionBindingExpression) switch + { + IDictionary p => p, + QueryableJsonProjectionInfo pi => pi.PropertyIndexMap, + _ => throw new UnreachableException() + }; _materializationContextBindings[parameterExpression] = propertyMap; _entityTypeIdentifyingExpressionInfo[parameterExpression] = @@ -585,109 +587,139 @@ protected override Expression VisitExtension(Expression extensionExpression) // by creating every entity every time we guarantee this doesn't happen if (!_isTracking || !_variableShaperMapping.TryGetValue(projectionBindingExpression, out var accessor)) { - if (GetProjectionIndex(projectionBindingExpression) is JsonProjectionInfo jsonProjectionInfo) + switch (GetProjectionIndex(projectionBindingExpression)) { - Check.DebugAssert(shaper.StructuralType is IEntityType, "JsonProjectionInfo over a complex type"); - var entityType = (IEntityType)shaper.StructuralType; - - if (_isTracking) + case JsonProjectionInfo jsonProjectionInfo: { - throw new InvalidOperationException(CoreStrings.OwnedEntitiesCannotBeTrackedWithoutTheirOwner); - } - - // json entity at the root - var (jsonReaderDataVariable, keyValuesParameter) = JsonShapingPreProcess( - jsonProjectionInfo, - entityType, - isCollection: false); + if (_isTracking) + { + // TODO: Update + throw new InvalidOperationException(CoreStrings.OwnedEntitiesCannotBeTrackedWithoutTheirOwner); + } - var shaperResult = CreateJsonShapers( - entityType, - shaper.IsNullable, - jsonReaderDataVariable, - keyValuesParameter, - parentEntityExpression: null, - navigation: null); + // json entity at the root + var (jsonReaderDataVariable, keyValuesParameter) = JsonShapingPreProcess( + jsonProjectionInfo, + shaper.StructuralType, + isCollection: false); - var visitedShaperResult = Visit(shaperResult); - var visitedShaperResultParameter = Parameter(visitedShaperResult.Type); - _variables.Add(visitedShaperResultParameter); - _jsonEntityExpressions.Add(Assign(visitedShaperResultParameter, visitedShaperResult)); + var shaperResult = CreateJsonShapers( + shaper.StructuralType, + shaper.IsNullable, + jsonReaderDataVariable, + keyValuesParameter, + containerEntityExpression: null, + relationship: null); - accessor = CompensateForCollectionMaterialization( - visitedShaperResultParameter, - shaper.Type); - } - else if (GetProjectionIndex(projectionBindingExpression) is QueryableJsonProjectionInfo - queryableJsonEntityProjectionInfo) - { - if (_isTracking) - { - throw new InvalidOperationException(CoreStrings.OwnedEntitiesCannotBeTrackedWithoutTheirOwner); + var visitedShaperResult = Visit(shaperResult); + var visitedShaperResultParameter = Parameter(visitedShaperResult.Type); + _variables.Add(visitedShaperResultParameter); + _jsonEntityExpressions.Add(Assign(visitedShaperResultParameter, visitedShaperResult)); + + accessor = CompensateForCollectionMaterialization( + visitedShaperResultParameter, + shaper.Type); + break; } - // json entity converted to query root and projected - var entityParameter = Parameter(shaper.Type); - _variables.Add(entityParameter); - var entityMaterializationExpression = (BlockExpression)_parentVisitor.InjectEntityMaterializers(shaper); + case QueryableJsonProjectionInfo queryableJsonEntityProjectionInfo: + { + if (_isTracking) + { + throw new InvalidOperationException(CoreStrings.OwnedEntitiesCannotBeTrackedWithoutTheirOwner); + } - var mappedProperties = queryableJsonEntityProjectionInfo.PropertyIndexMap.Keys.ToList(); - var rewrittenEntityMaterializationExpression = new QueryableJsonEntityMaterializerRewriter(mappedProperties) - .Rewrite(entityMaterializationExpression); + // json entity converted to query root and projected + var entityParameter = Parameter(shaper.Type); + _variables.Add(entityParameter); + var entityMaterializationExpression = (BlockExpression)_parentVisitor.InjectStructuralTypeMaterializers(shaper); - var visitedEntityMaterializationExpression = Visit(rewrittenEntityMaterializationExpression); - _expressions.Add(Assign(entityParameter, visitedEntityMaterializationExpression)); + var mappedProperties = queryableJsonEntityProjectionInfo.PropertyIndexMap.Keys.ToList(); + Check.DebugAssert(mappedProperties.All(p => p is IProperty)); + var rewrittenEntityMaterializationExpression = new QueryableJsonEntityMaterializerRewriter(mappedProperties) + .Rewrite(entityMaterializationExpression); - foreach (var childProjectionInfo in queryableJsonEntityProjectionInfo.ChildrenProjectionInfo) - { - var (jsonReaderDataVariable, keyValuesParameter) = JsonShapingPreProcess( - childProjectionInfo.JsonProjectionInfo, - childProjectionInfo.Navigation.TargetEntityType, - childProjectionInfo.Navigation.IsCollection); + var visitedEntityMaterializationExpression = Visit(rewrittenEntityMaterializationExpression); + _expressions.Add(Assign(entityParameter, visitedEntityMaterializationExpression)); - var shaperResult = CreateJsonShapers( - childProjectionInfo.Navigation.TargetEntityType, - nullable: true, - jsonReaderDataVariable, - keyValuesParameter, - parentEntityExpression: entityParameter, - navigation: childProjectionInfo.Navigation); + foreach (var childProjectionInfo in queryableJsonEntityProjectionInfo.ChildrenProjectionInfo) + { + var (jsonReaderDataVariable, keyValuesParameter) = JsonShapingPreProcess( + childProjectionInfo.JsonProjectionInfo, + childProjectionInfo.Navigation.TargetEntityType, + childProjectionInfo.Navigation.IsCollection); + + var shaperResult = CreateJsonShapers( + childProjectionInfo.Navigation.TargetEntityType, + nullable: true, + jsonReaderDataVariable, + keyValuesParameter, + containerEntityExpression: entityParameter, + childProjectionInfo.Navigation); + + var visitedShaperResult = Visit(shaperResult); + + _includeExpressions.Add(visitedShaperResult); + } - var visitedShaperResult = Visit(shaperResult); + accessor = CompensateForCollectionMaterialization( + entityParameter, + shaper.Type); - _includeExpressions.Add(visitedShaperResult); + break; } - accessor = CompensateForCollectionMaterialization( - entityParameter, - shaper.Type); - } - else - { - var entityParameter = Parameter(shaper.Type, "entity"); - _variables.Add(entityParameter); - if (shaper.StructuralType is IEntityType entityType - && entityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy) + default: { - var concreteTypes = entityType.GetDerivedTypesInclusive().Where(e => !e.IsAbstract()).ToArray(); - // Single concrete TPC entity type won't have discriminator column. - // We store the value here and inject it directly rather than reading from server. - if (concreteTypes.Length == 1) + var entityParameter = Parameter(shaper.Type, "entity"); + _variables.Add(entityParameter); + if (shaper.StructuralType is IEntityType entityType + && entityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy) { - _singleEntityTypeDiscriminatorValues[ - projectionBindingExpression] - = concreteTypes[0].ShortName(); + var concreteTypes = entityType.GetDerivedTypesInclusive().Where(e => !e.IsAbstract()).ToArray(); + // Single concrete TPC entity type won't have discriminator column. + // We store the value here and inject it directly rather than reading from server. + if (concreteTypes.Length == 1) + { + _singleEntityTypeDiscriminatorValues[projectionBindingExpression] = concreteTypes[0].ShortName(); + } } - } - var entityMaterializationExpression = _parentVisitor.InjectEntityMaterializers(shaper); - entityMaterializationExpression = Visit(entityMaterializationExpression); + var entityMaterializationExpression = _parentVisitor.InjectStructuralTypeMaterializers(shaper); + entityMaterializationExpression = Visit(entityMaterializationExpression); - _expressions.Add(Assign(entityParameter, entityMaterializationExpression)); + _expressions.Add(Assign(entityParameter, entityMaterializationExpression)); + + accessor = CompensateForCollectionMaterialization( + entityParameter, + shaper.Type); + + if (GetProjectionIndex(projectionBindingExpression) is IDictionary propertyMap) + { + foreach (var (property, projectionIndex) in propertyMap) + { + if (property is IComplexProperty { ComplexType: var complexType } complexProperty + && complexType.IsMappedToJson()) + { + var jsonReaderDataVariable = GenerateJsonReader(projectionIndex, complexType); + + var shaperResult = CreateJsonShapers( + complexType, + nullable: true, // TODO + jsonReaderDataVariable, + keyValuesParameter: null!, // TODO + containerEntityExpression: entityParameter, + relationship: complexProperty); + + var visitedShaperResult = Visit(shaperResult); + + _includeExpressions.Add(visitedShaperResult); + } + } + } - accessor = CompensateForCollectionMaterialization( - entityParameter, - shaper.Type); + break; + } } if (_isTracking) @@ -716,45 +748,56 @@ protected override Expression VisitExtension(Expression extensionExpression) } } - var entityMaterializationExpression = _parentVisitor.InjectEntityMaterializers(shaper); + var entityMaterializationExpression = _parentVisitor.InjectStructuralTypeMaterializers(shaper); entityMaterializationExpression = Visit(entityMaterializationExpression); return entityMaterializationExpression; } - case CollectionResultExpression { Navigation: INavigation navigation } collectionResultExpression - when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression) - is JsonProjectionInfo jsonProjectionInfo: + case CollectionResultExpression + { + QueryExpression: ProjectionBindingExpression projectionBindingExpression, + Relationship: IPropertyBase relationship, + } collectionResult + when GetProjectionIndex(projectionBindingExpression) is JsonProjectionInfo jsonProjectionInfo: { if (_isTracking) { throw new InvalidOperationException(CoreStrings.OwnedEntitiesCannotBeTrackedWithoutTheirOwner); } + var relatedStructuralType = relationship switch + { + IComplexProperty complexProperty => (ITypeBase)complexProperty.ComplexType, + INavigation navigation => navigation.TargetEntityType, + + _ => throw new UnreachableException() + }; + // json entity collection at the root var (jsonReaderDataVariable, keyValuesParameter) = JsonShapingPreProcess( jsonProjectionInfo, - navigation.TargetEntityType, + relatedStructuralType, isCollection: true); var shaperResult = CreateJsonShapers( - navigation.TargetEntityType, + relatedStructuralType, nullable: true, jsonReaderDataVariable, keyValuesParameter, - parentEntityExpression: null, - navigation: navigation); + containerEntityExpression: null, + relationship); var visitedShaperResult = Visit(shaperResult); - var jsonCollectionParameter = Parameter(collectionResultExpression.Type); + var jsonCollectionParameter = Parameter(collectionResult.Type); _variables.Add(jsonCollectionParameter); _jsonEntityExpressions.Add(Assign(jsonCollectionParameter, visitedShaperResult)); return CompensateForCollectionMaterialization( jsonCollectionParameter, - collectionResultExpression.Type); + collectionResult.Type); } case ProjectionBindingExpression projectionBindingExpression @@ -898,7 +941,7 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression) outerIdentifierLambdaCompiled, _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant( navigation, - LiftableConstantExpressionHelpers.BuildNavigationAccessLambda(navigation), + LiftableConstantExpressionHelpers.BuildRelationshipAccessLambda(navigation), navigation.Name + "Navigation", typeof(INavigationBase)), navigation.IsShadowProperty() @@ -962,7 +1005,7 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression) innerShaper, _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant( inverseNavigation, - LiftableConstantExpressionHelpers.BuildNavigationAccessLambda(inverseNavigation), + LiftableConstantExpressionHelpers.BuildRelationshipAccessLambda(inverseNavigation), (inverseNavigation?.Name ?? "null") + "InverseNavigation", typeof(INavigationBase)), GenerateFixup(includingEntityClrType, relatedEntityClrType, navigation, inverseNavigation), @@ -1036,7 +1079,7 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression) parentIdentifierLambdaCompiled, _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant( navigation, - LiftableConstantExpressionHelpers.BuildNavigationAccessLambda(navigation), + LiftableConstantExpressionHelpers.BuildRelationshipAccessLambda(navigation), navigation.Name + "Navigation", typeof(INavigationBase)), _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant( @@ -1084,7 +1127,7 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression) : typeof(Action)), _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant( inverseNavigation, - LiftableConstantExpressionHelpers.BuildNavigationAccessLambda(inverseNavigation), + LiftableConstantExpressionHelpers.BuildRelationshipAccessLambda(inverseNavigation), (inverseNavigation?.Name ?? "null") + "InverseNavigation", typeof(INavigationBase)), GenerateFixup(includingEntityClrType, relatedEntityClrType, navigation, inverseNavigation), @@ -1093,10 +1136,9 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression) else { var projectionBindingExpression = (includeExpression.NavigationExpression as CollectionResultExpression) - ?.ProjectionBindingExpression + ?.QueryExpression as ProjectionBindingExpression ?? (includeExpression.NavigationExpression as RelationalStructuralTypeShaperExpression) - ?.ValueBufferExpression as - ProjectionBindingExpression; + ?.ValueBufferExpression as ProjectionBindingExpression; // json include case if (projectionBindingExpression != null @@ -1112,8 +1154,8 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression) nullable: true, jsonReaderDataVariable, keyValuesParameter, - parentEntityExpression: entity, - navigation: (INavigation)includeExpression.Navigation); + containerEntityExpression: entity, + (INavigation)includeExpression.Navigation); var visitedShaperResult = Visit(shaperResult); @@ -1141,12 +1183,12 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression) navigationExpression, _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant( navigation, - LiftableConstantExpressionHelpers.BuildNavigationAccessLambda(navigation), + LiftableConstantExpressionHelpers.BuildRelationshipAccessLambda(navigation), navigation.Name + "Navigation", typeof(INavigation)), _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant( inverseNavigation, - LiftableConstantExpressionHelpers.BuildNavigationAccessLambda(inverseNavigation), + LiftableConstantExpressionHelpers.BuildRelationshipAccessLambda(inverseNavigation), (inverseNavigation?.Name ?? "null") + "InverseNavigation", typeof(INavigation)), GenerateFixup(includingType, relatedEntityType, navigation, inverseNavigation), @@ -1454,8 +1496,8 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp var property = methodCallExpression.Arguments[2].GetConstantValue(); var mappingParameter = (ParameterExpression)((MethodCallExpression)methodCallExpression.Arguments[0]).Object!; - if (_jsonMaterializationContextToJsonReaderDataAndKeyValuesParameterMapping.TryGetValue( - mappingParameter, out var mappedParameter)) + if (_jsonMaterializationContextToJsonReaderDataAndKeyValuesParameterMapping + .TryGetValue(mappingParameter, out var mappedParameter)) { var (jsonReaderDataParameter, keyPropertyValuesParameter) = mappedParameter; @@ -1518,16 +1560,17 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp } private Expression CreateJsonShapers( - IEntityType entityType, + ITypeBase structuralType, bool nullable, ParameterExpression jsonReaderDataParameter, - ParameterExpression keyValuesParameter, - Expression? parentEntityExpression, - INavigation? navigation) + Expression? keyValuesParameter, + Expression? containerEntityExpression, + IPropertyBase? relationship) { var jsonReaderDataShaperLambdaParameter = Parameter(typeof(JsonReaderData)); // TODO: Use ISnapshot instead #26544 var keyValuesShaperLambdaParameter = Parameter(typeof(object[])); + keyValuesParameter ??= Constant(null, typeof(object[])); // For complex types var shaperBlockVariables = new List(); var shaperBlockExpressions = new List(); @@ -1536,47 +1579,72 @@ private Expression CreateJsonShapers( _jsonValueBufferToJsonReaderDataAndKeyValuesParameterMapping[valueBufferParameter] = (jsonReaderDataShaperLambdaParameter, keyValuesShaperLambdaParameter); - var entityShaperExpression = new RelationalStructuralTypeShaperExpression( - entityType, + var structuralTypeShaperExpression = new RelationalStructuralTypeShaperExpression( + structuralType, valueBufferParameter, nullable); - var entityShaperMaterializer = (BlockExpression)_parentVisitor.InjectEntityMaterializers(entityShaperExpression); + var structuralTypeShaperMaterializer = + (BlockExpression)_parentVisitor.InjectStructuralTypeMaterializers(structuralTypeShaperExpression); var innerShapersMap = new Dictionary(); var innerFixupMap = new Dictionary(); var trackingInnerFixupMap = new Dictionary(); - foreach (var ownedNavigation in entityType.GetNavigations().Where( - n => n.TargetEntityType.IsMappedToJson() && n.ForeignKey.IsOwnership && n == n.ForeignKey.PrincipalToDependent)) + + // Go over all relationships (complex properties and navigations - if we're an (owned) entity), which represent JSON + // nested types; generate shapers and fixup to wire the materialized related instance into the parent's property. + // Note that we need to build entity shapers and fixup separately; we don't know the order in which data comes, so + // we need to read through everything before we can do fixup safely + IEnumerable nestedRelationships = structuralType.GetComplexProperties(); + + if (structuralType is IEntityType entityType) { - Debug.Assert(!ownedNavigation.IsOnDependent, "JSON navigations should always be from principal do dependent"); + nestedRelationships = nestedRelationships.Concat( + entityType.GetNavigations() + .Where(n => n.TargetEntityType.IsMappedToJson() && n.ForeignKey.IsOwnership && n == n.ForeignKey.PrincipalToDependent)); + } + + foreach (var nestedRelationship in nestedRelationships) + { + Check.DebugAssert( + nestedRelationship is not INavigation ownedNavigation || !ownedNavigation.IsOnDependent, + "JSON navigations should always be from principal do dependent"); + + Check.DebugAssert( + nestedRelationship is not IComplexProperty { ComplexType: var complexType } || complexType.IsMappedToJson(), + "Non-JSON complex type within JSON complex type"); + + var (relatedStructuralType, inverseNavigation, isRelationshipNullable) = nestedRelationship switch + { + INavigation n => ((ITypeBase)n.TargetEntityType, n.Inverse, !n.ForeignKey.IsRequiredDependent), + IComplexProperty cp => (cp.ComplexType, null, cp.IsNullable), + + _ => throw new UnreachableException() + }; - // we need to build entity shapers and fixup separately - // we don't know the order in which data comes, so we need to read through everything - // before we can do fixup safely var innerShaper = CreateJsonShapers( - ownedNavigation.TargetEntityType, - nullable || !ownedNavigation.ForeignKey.IsRequiredDependent, + relatedStructuralType, + nullable || isRelationshipNullable, jsonReaderDataShaperLambdaParameter, keyValuesShaperLambdaParameter, - parentEntityExpression: null, - navigation: ownedNavigation); + containerEntityExpression: null, + nestedRelationship); - var navigationJsonPropertyName = ownedNavigation.TargetEntityType.GetJsonPropertyName()!; + var navigationJsonPropertyName = relatedStructuralType.GetJsonPropertyName()!; innerShapersMap[navigationJsonPropertyName] = innerShaper; - if (ownedNavigation.IsCollection) + if (nestedRelationship.IsCollection) { - var shaperEntityParameter = Parameter(ownedNavigation.DeclaringEntityType.ClrType); - var ownedNavigationType = ownedNavigation.GetMemberInfo(forMaterialization: true, forSet: true).GetMemberType(); + var shaperEntityParameter = Parameter(structuralType.ClrType); + var ownedNavigationType = nestedRelationship.GetMemberInfo(forMaterialization: true, forSet: true).GetMemberType(); var shaperCollectionParameter = Parameter(ownedNavigationType); var expressions = new List(); var expressionsForTracking = new List(); - if (!ownedNavigation.IsShadowProperty()) + if (!nestedRelationship.IsShadowProperty()) { expressions.Add( - shaperEntityParameter.MakeMemberAccess(ownedNavigation.GetMemberInfo(forMaterialization: true, forSet: true)) + shaperEntityParameter.MakeMemberAccess(nestedRelationship.GetMemberInfo(forMaterialization: true, forSet: true)) .Assign(shaperCollectionParameter)); expressionsForTracking.Add( @@ -1588,12 +1656,11 @@ private Expression CreateJsonShapers( typeof(ShaperProcessingExpressionVisitor).GetMethod(nameof(Any))!, shaperCollectionParameter))), shaperEntityParameter - .MakeMemberAccess(ownedNavigation.GetMemberInfo(forMaterialization: true, forSet: true)) + .MakeMemberAccess(nestedRelationship.GetMemberInfo(forMaterialization: true, forSet: true)) .Assign(shaperCollectionParameter))); } - if (ownedNavigation.Inverse is INavigation inverseNavigation - && !inverseNavigation.IsShadowProperty()) + if (inverseNavigation is not null && !inverseNavigation.IsShadowProperty()) { var innerFixupCollectionElementParameter = Parameter(inverseNavigation.DeclaringEntityType.ClrType); var innerFixupParentParameter = Parameter(inverseNavigation.TargetEntityType.ClrType); @@ -1601,7 +1668,7 @@ private Expression CreateJsonShapers( var elementFixup = Lambda( Block( typeof(void), - AssignReferenceNavigation( + AssignReferenceRelationship( innerFixupCollectionElementParameter, innerFixupParentParameter, inverseNavigation)), @@ -1636,27 +1703,27 @@ private Expression CreateJsonShapers( else { var fixup = GenerateReferenceFixupForJson( - ownedNavigation.DeclaringEntityType.ClrType, - ownedNavigation.TargetEntityType.ClrType, - ownedNavigation, - ownedNavigation.Inverse); + structuralType.ClrType, + relatedStructuralType.ClrType, + nestedRelationship, + inverseNavigation); innerFixupMap[navigationJsonPropertyName] = fixup; } } var rewrittenEntityShaperMaterializer = new JsonEntityMaterializerRewriter( - entityType, + structuralType, _queryStateManager, jsonReaderDataShaperLambdaParameter, innerShapersMap, innerFixupMap, trackingInnerFixupMap, _queryLogger, - _parentVisitor.Dependencies.LiftableConstantFactory).Rewrite(entityShaperMaterializer); + _parentVisitor.Dependencies.LiftableConstantFactory).Rewrite(structuralTypeShaperMaterializer); var entityShaperMaterializerVariable = Variable( - entityShaperMaterializer.Type, + structuralTypeShaperMaterializer.Type, "entityShaperMaterializer"); shaperBlockVariables.Add(entityShaperMaterializerVariable); @@ -1672,57 +1739,57 @@ private Expression CreateJsonShapers( keyValuesShaperLambdaParameter, jsonReaderDataShaperLambdaParameter); - if (parentEntityExpression != null) + if (containerEntityExpression is not null) { // this happens only on top level when we project owner entity in this case we can do fixup as part of generating materializer // (since we are guaranteed that the parent already exists) - for nested JSON materialization we need to do fixup at the end // because we are streaming the data and don't know if we get the parent json object before the child // (in case parent ctor takes some parameters and they are read as last thing in the JSON) - Check.DebugAssert(navigation != null, "Navigation shouldn't be null when including."); + Check.DebugAssert(relationship is not null, "relationship shouldn't be null when including."); + + var declaringClrType = relationship.DeclaringType.ClrType; + var relatedClrType = relationship switch + { + INavigation n => n.TargetEntityType.ClrType, + IComplexProperty cp => cp.ComplexType.ClrType, + _ => throw new InvalidOperationException("Unsupported type for JSON materialization.") + }; var fixup = GenerateFixup( - navigation.DeclaringEntityType.ClrType, - navigation.TargetEntityType.ClrType, - navigation, - navigation.Inverse); + relationship.DeclaringType.ClrType, + relatedClrType, + relationship, + relationship is INavigation navigation ? navigation.Inverse : null); - // inheritance scenario - navigation defined on derived - var includingEntityExpression = parentEntityExpression.Type != navigation.DeclaringEntityType.ClrType - ? Convert(parentEntityExpression, navigation.DeclaringEntityType.ClrType) - : parentEntityExpression; + // inheritance scenario - navigation/complex property defined on derived + var includingEntityExpression = containerEntityExpression.Type != declaringClrType + ? Convert(containerEntityExpression, declaringClrType) + : containerEntityExpression; - if (navigation.IsCollection) + if (relationship.IsCollection) { var includeJsonEntityCollectionMethodCall = Call( - IncludeJsonEntityCollectionMethodInfo.MakeGenericMethod( - navigation.DeclaringEntityType.ClrType, - navigation.TargetEntityType.ClrType), + IncludeJsonEntityCollectionMethodInfo.MakeGenericMethod(declaringClrType, relatedClrType), QueryCompilationContext.QueryContextParameter, keyValuesParameter, jsonReaderDataParameter, includingEntityExpression, shaperLambda, - GetOrCreateCollectionObjectLambda( - navigation.DeclaringEntityType.ClrType, - navigation), + GetOrCreateCollectionObjectLambda(declaringClrType, relationship), fixup, Constant(_isTracking)); - return navigation.DeclaringEntityType.ClrType.IsAssignableFrom(parentEntityExpression.Type) + return declaringClrType.IsAssignableFrom(containerEntityExpression.Type) ? includeJsonEntityCollectionMethodCall : IfThen( - TypeIs( - parentEntityExpression, - navigation.DeclaringEntityType.ClrType), + TypeIs(containerEntityExpression, declaringClrType), includeJsonEntityCollectionMethodCall); } var includeJsonEntityReferenceMethodCall = Call( - IncludeJsonEntityReferenceMethodInfo.MakeGenericMethod( - navigation.DeclaringEntityType.ClrType, - navigation.TargetEntityType.ClrType), + IncludeJsonEntityReferenceMethodInfo.MakeGenericMethod(declaringClrType, relatedClrType), QueryCompilationContext.QueryContextParameter, keyValuesParameter, jsonReaderDataParameter, @@ -1731,38 +1798,41 @@ private Expression CreateJsonShapers( fixup, Constant(_isTracking)); - return navigation.DeclaringEntityType.ClrType.IsAssignableFrom(parentEntityExpression.Type) + return declaringClrType.IsAssignableFrom(containerEntityExpression.Type) ? includeJsonEntityReferenceMethodCall : IfThen( - TypeIs( - parentEntityExpression, - navigation.DeclaringEntityType.ClrType), + TypeIs(containerEntityExpression, declaringClrType), includeJsonEntityReferenceMethodCall); } - if (navigation is { IsCollection: true }) + if (relationship is { IsCollection: true }) { - var collectionClrType = navigation.GetMemberInfo(forMaterialization: true, forSet: true).GetMemberType(); + var collectionClrType = relationship.GetMemberInfo(forMaterialization: true, forSet: true).GetMemberType(); var materializeJsonEntityCollectionMethodCall = Call( MaterializeJsonEntityCollectionMethodInfo.MakeGenericMethod( - navigation.TargetEntityType.ClrType, + relationship switch + { + INavigation n => n.TargetEntityType.ClrType, + IComplexProperty cp => cp.ComplexType.ClrType, + _ => throw new InvalidOperationException("Unsupported type for JSON materialization.") + }, collectionClrType), QueryCompilationContext.QueryContextParameter, keyValuesParameter, jsonReaderDataParameter, _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant( - navigation, - LiftableConstantExpressionHelpers.BuildNavigationAccessLambda(navigation), - navigation.Name + "Navigation", - typeof(INavigation)), + relationship, + LiftableConstantExpressionHelpers.BuildRelationshipAccessLambda(relationship), + relationship.Name + "Relationship", + typeof(IPropertyBase)), shaperLambda); return materializeJsonEntityCollectionMethodCall; } var materializedRootJsonEntity = Call( - MaterializeJsonEntityMethodInfo.MakeGenericMethod(entityType.ClrType), + MaterializeJsonEntityMethodInfo.MakeGenericMethod(structuralType.ClrType), QueryCompilationContext.QueryContextParameter, keyValuesParameter, jsonReaderDataParameter, @@ -1773,7 +1843,7 @@ private Expression CreateJsonShapers( } private sealed class JsonEntityMaterializerRewriter( - IEntityType entityType, + ITypeBase structuralType, bool queryStateManager, ParameterExpression jsonReaderDataParameter, IDictionary innerShapersMap, @@ -1798,7 +1868,7 @@ public BlockExpression Rewrite(BlockExpression jsonEntityShaperMaterializer) protected override Expression VisitSwitch(SwitchExpression switchExpression) { - if (switchExpression.SwitchValue.Type == typeof(IEntityType) + if (switchExpression.SwitchValue.Type.IsAssignableTo(typeof(ITypeBase)) && switchExpression is { Cases: @@ -1809,10 +1879,10 @@ protected override Expression VisitSwitch(SwitchExpression switchExpression) } ] } - && onlyValueExpression.GetConstantValue() == entityType) + && onlyValueExpression.GetConstantValue() == structuralType) { var valueBufferTryReadValueMethodsToProcess = - new ValueBufferTryReadValueMethodsFinder(entityType).FindValueBufferTryReadValueMethods(body); + new ValueBufferTryReadValueMethodsFinder(structuralType).FindValueBufferTryReadValueMethods(body); BlockExpression jsonEntityTypeInitializerBlock; //sometimes we have shadow snapshot and sometimes not, but type initializer always comes last @@ -1885,7 +1955,7 @@ protected override Expression VisitSwitch(SwitchExpression switchExpression) var tokenTypeVariable = Variable(typeof(JsonTokenType), "tokenType"); var jsonEntityTypeVariable = (ParameterExpression)jsonEntityTypeInitializerBlock.Expressions[^1]; - Debug.Assert(jsonEntityTypeVariable.Type == entityType.ClrType); + Debug.Assert(jsonEntityTypeVariable.Type == structuralType.ClrType); var finalBlockVariables = new List { @@ -2190,7 +2260,7 @@ protected override Expression VisitConditional(ConditionalExpression conditional var instanceAssignment = ifFalseBlock.Expressions.OfType().Single( e => e is { NodeType: ExpressionType.Assign, Left: ParameterExpression instance, Right: BlockExpression } - && instance.Type == entityType.ClrType); + && instance.Type == structuralType.ClrType); var instanceAssignmentBody = (BlockExpression)instanceAssignment.Right; var newInstanceAssignmentVariables = instanceAssignmentBody.Variables.ToList(); @@ -2284,8 +2354,8 @@ private sealed class ValueBufferTryReadValueMethodsFinder : ExpressionVisitor private readonly List _nonKeyProperties; private readonly List _valueBufferTryReadValueMethods = []; - public ValueBufferTryReadValueMethodsFinder(IEntityType entityType) - => _nonKeyProperties = entityType.GetProperties().Where(p => !p.IsPrimaryKey()).ToList(); + public ValueBufferTryReadValueMethodsFinder(ITypeBase structuralType) + => _nonKeyProperties = structuralType.GetProperties().Where(p => !p.IsPrimaryKey()).ToList(); public List FindValueBufferTryReadValueMethods(Expression expression) { @@ -2328,7 +2398,7 @@ protected override Expression VisitBinary(BinaryExpression node) && !property.ClrType.IsArray) { #pragma warning disable EF1001 // Internal EF Core API usage. - var genericMethod = EntityMaterializerSource.PopulateListMethod.MakeGenericMethod( + var genericMethod = StructuralTypeMaterializerSource.PopulateListMethod.MakeGenericMethod( property.ClrType.TryGetElementType(typeof(IEnumerable<>))!); #pragma warning restore EF1001 // Internal EF Core API usage. var currentVariable = Variable(parameter!.Type); @@ -2391,14 +2461,13 @@ private bool IsPropertyAssignment( } } - private (ParameterExpression, ParameterExpression) JsonShapingPreProcess( - JsonProjectionInfo jsonProjectionInfo, - IEntityType entityType, - bool isCollection) + private ParameterExpression GenerateJsonReader(int jsonColumnIndex, ITypeBase structuralType) { - var jsonColumnName = entityType.GetContainerColumnName()!; - var jsonColumnTypeMapping = (entityType.GetViewOrTableMappings().SingleOrDefault()?.Table - ?? entityType.GetDefaultMappings().Single().Table) + Check.DebugAssert(structuralType.IsMappedToJson()); + + var jsonColumnName = structuralType.GetContainerColumnName()!; + var jsonColumnTypeMapping = (structuralType.ContainingEntityType.GetViewOrTableMappings().SingleOrDefault()?.Table + ?? structuralType.GetDefaultMappings().Single().Table) .FindColumn(jsonColumnName)!.StoreTypeMapping; var jsonStreamVariable = Variable(typeof(Stream), "jsonStream"); @@ -2409,7 +2478,7 @@ private bool IsPropertyAssignment( jsonStreamVariable, CreateGetValueExpression( _dataReaderParameter, - jsonProjectionInfo.JsonColumnIndex, + jsonColumnIndex, nullable: true, jsonColumnTypeMapping, typeof(MemoryStream), @@ -2454,14 +2523,27 @@ private bool IsPropertyAssignment( _expressions.Add(jsonReaderDataAssignment); _expressions.Add(jsonReaderManagerBlock); + return jsonReaderDataVariable; + } + + private (ParameterExpression, ParameterExpression) JsonShapingPreProcess( + JsonProjectionInfo jsonProjectionInfo, + ITypeBase structuralType, + bool isCollection) + { + var jsonReaderDataVariable = GenerateJsonReader(jsonProjectionInfo.JsonColumnIndex, structuralType); + // we should have keyAccessInfo for every PK property of the entity, unless we are generating shaper for the collection // in that case the final key property will be synthesized in the shaper code - var expectedKeyValuesCount = entityType.FindPrimaryKey()!.Properties.Count - (isCollection ? 1 : 0); + var expectedKeyValuesCount = structuralType is IEntityType entityType + ? entityType.FindPrimaryKey()!.Properties.Count - (isCollection ? 1 : 0) + : 0; + var keyValues = new Expression[expectedKeyValuesCount]; if (keyValues.Length != expectedKeyValuesCount && !_isTracking) { - throw new InvalidOperationException(RelationalStrings.JsonEntityMissingKeyInformation(entityType.ShortName())); + throw new InvalidOperationException(RelationalStrings.JsonEntityMissingKeyInformation(structuralType.ShortName())); } //var keyValues = new Expression[jsonProjectionInfo.KeyAccessInfo.Count]; @@ -2552,7 +2634,7 @@ ParameterExpression ExtractAndCacheNonConstantJsonArrayElementAccessValue(int in } } - private sealed class QueryableJsonEntityMaterializerRewriter(List mappedProperties) : ExpressionVisitor + private sealed class QueryableJsonEntityMaterializerRewriter(List mappedProperties) : ExpressionVisitor { public BlockExpression Rewrite(BlockExpression jsonEntityShaperMaterializer) => (BlockExpression)VisitBlock(jsonEntityShaperMaterializer); @@ -2606,21 +2688,21 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp } private LambdaExpression GenerateFixup( - Type entityType, - Type relatedEntityType, - INavigationBase navigation, + Type structuralClrType, + Type relatedStructuralClrType, + IPropertyBase relationship, INavigationBase? inverseNavigation) { - var entityParameter = Parameter(entityType); - var relatedEntityParameter = Parameter(relatedEntityType); + var entityParameter = Parameter(structuralClrType); + var relatedEntityParameter = Parameter(relatedStructuralClrType); var expressions = new List(); - if (!navigation.IsShadowProperty()) + if (!relationship.IsShadowProperty()) { expressions.Add( - navigation.IsCollection - ? AddToCollectionNavigation(entityParameter, relatedEntityParameter, navigation) - : AssignReferenceNavigation(entityParameter, relatedEntityParameter, navigation)); + relationship.IsCollection + ? AddToCollectionRelationship(entityParameter, relatedEntityParameter, relationship) + : AssignReferenceRelationship(entityParameter, relatedEntityParameter, relationship)); } if (inverseNavigation != null @@ -2628,8 +2710,8 @@ private LambdaExpression GenerateFixup( { expressions.Add( inverseNavigation.IsCollection - ? AddToCollectionNavigation(relatedEntityParameter, entityParameter, inverseNavigation) - : AssignReferenceNavigation(relatedEntityParameter, entityParameter, inverseNavigation)); + ? AddToCollectionRelationship(relatedEntityParameter, entityParameter, inverseNavigation) + : AssignReferenceRelationship(relatedEntityParameter, entityParameter, inverseNavigation)); } return Lambda(Block(typeof(void), expressions), entityParameter, relatedEntityParameter); @@ -2638,27 +2720,27 @@ private LambdaExpression GenerateFixup( private static LambdaExpression GenerateReferenceFixupForJson( Type entityType, Type relatedEntityType, - INavigationBase navigation, + IPropertyBase relationship, INavigationBase? inverseNavigation) { var entityParameter = Parameter(entityType); var relatedEntityParameter = Parameter(relatedEntityType); var expressions = new List(); - if (!navigation.IsShadowProperty()) + if (!relationship.IsShadowProperty()) { expressions.Add( - AssignReferenceNavigation( + AssignReferenceRelationship( entityParameter, relatedEntityParameter, - navigation)); + relationship)); } if (inverseNavigation != null && !inverseNavigation.IsShadowProperty()) { expressions.Add( - AssignReferenceNavigation( + AssignReferenceRelationship( relatedEntityParameter, entityParameter, inverseNavigation)); @@ -2685,15 +2767,13 @@ public static void InverseCollectionFixup( } } - private static Expression AssignReferenceNavigation( + private static Expression AssignReferenceRelationship( ParameterExpression entity, ParameterExpression relatedEntity, - INavigationBase navigation) - => entity.MakeMemberAccess(navigation.GetMemberInfo(forMaterialization: true, forSet: true)).Assign(relatedEntity); + IPropertyBase relationship) + => entity.MakeMemberAccess(relationship.GetMemberInfo(forMaterialization: true, forSet: true)).Assign(relatedEntity); - private Expression GetOrCreateCollectionObjectLambda( - Type entityType, - INavigationBase navigation) + private Expression GetOrCreateCollectionObjectLambda(Type entityType, IPropertyBase relationship) { var prm = Parameter(entityType); @@ -2702,9 +2782,9 @@ private Expression GetOrCreateCollectionObjectLambda( typeof(void), Call( _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant( - navigation.GetCollectionAccessor(), - LiftableConstantExpressionHelpers.BuildClrCollectionAccessorLambda(navigation), - navigation.Name + "NavigationCollectionAccessor", + relationship.GetCollectionAccessor(), + LiftableConstantExpressionHelpers.BuildClrCollectionAccessorLambda(relationship), + relationship.Name + "RelationshipCollectionAccessor", typeof(IClrCollectionAccessor)), CollectionAccessorGetOrCreateMethodInfo, prm, @@ -2712,15 +2792,15 @@ private Expression GetOrCreateCollectionObjectLambda( prm); } - private Expression AddToCollectionNavigation( + private Expression AddToCollectionRelationship( ParameterExpression entity, ParameterExpression relatedEntity, - INavigationBase navigation) + IPropertyBase relationship) => Call( _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant( - navigation.GetCollectionAccessor(), - LiftableConstantExpressionHelpers.BuildClrCollectionAccessorLambda(navigation), - navigation.Name + "NavigationCollectionAccessor", + relationship.GetCollectionAccessor(), + LiftableConstantExpressionHelpers.BuildClrCollectionAccessorLambda(relationship), + relationship.Name + "RelationshipCollectionAccessor", typeof(IClrCollectionAccessor)), CollectionAccessorAddMethodInfo, entity, @@ -3286,7 +3366,7 @@ protected override Expression VisitExtension(Expression extensionExpression) if (extensionExpression is CollectionResultExpression { - ProjectionBindingExpression: ProjectionBindingExpression collectionProjectionBindingExpression + QueryExpression: ProjectionBindingExpression collectionProjectionBindingExpression } collectionResultExpression) { var collectionProjection = @@ -3300,9 +3380,10 @@ protected override Expression VisitExtension(Expression extensionExpression) RelationalStrings.JsonProjectingQueryableOperationNoTrackingWithIdentityResolution( nameof(QueryTrackingBehavior.NoTrackingWithIdentityResolution))); - case JsonProjectionInfo jsonCollectionProjectionInfo: + case JsonProjectionInfo jsonCollectionProjectionInfo when collectionResultExpression.Relationship is INavigation navigation: { - var jsonEntityType = collectionResultExpression.Navigation!.TargetEntityType; + var jsonEntityType = navigation.TargetEntityType; + if (_insideInclude) { if (!_includedJsonEntityTypes.Contains(jsonEntityType)) diff --git a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs index fb89e8c214e..bbcb020acee 100644 --- a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs @@ -139,6 +139,10 @@ protected virtual void AddTranslationErrorDetails(string details) StructuralTypeReferenceExpression { Parameter: StructuralTypeShaperExpression shaper } => shaper, + // Complex JSON collection getting projected out via Select + CollectionResultExpression c + => c, + StructuralTypeReferenceExpression { Subquery: not null } => null, // TODO: think about this - probably unsupported (if so, message) @@ -1335,21 +1339,42 @@ private SqlExpression BindProperty(StructuralTypeReferenceExpression typeReferen } } - private StructuralTypeReferenceExpression BindComplexProperty( - StructuralTypeReferenceExpression typeReference, - IComplexProperty complexProperty) + private Expression BindComplexProperty(StructuralTypeReferenceExpression typeReference, IComplexProperty complexProperty) { switch (typeReference) { case { Parameter: StructuralTypeShaperExpression shaper }: - var projection = (StructuralTypeProjectionExpression)Visit(shaper.ValueBufferExpression); + switch (Visit(shaper.ValueBufferExpression)) + { + case StructuralTypeProjectionExpression structuralTypeProjection: + // TODO: Move all this logic into StructuralTypeProjectionExpression, #31376 + Check.DebugAssert(structuralTypeProjection.IsNullable == shaper.IsNullable, "Nullability mismatch"); + + return structuralTypeProjection.BindComplexProperty(complexProperty) switch + { + StructuralTypeShaperExpression s => new StructuralTypeReferenceExpression(s), + CollectionResultExpression c => c, + + _ => throw new UnreachableException() + }; + + case JsonQueryExpression jsonQuery: + var nestedJsonQuery = jsonQuery.BindRelationship(complexProperty); - // TODO: Move all this logic into StructuralTypeProjectionExpression, #31376 - Check.DebugAssert(projection.IsNullable == shaper.IsNullable, "Nullability mismatch"); - return new StructuralTypeReferenceExpression(projection.BindComplexProperty(complexProperty)); + return complexProperty.IsCollection + ? new CollectionResultExpression(nestedJsonQuery, complexProperty, elementType: complexProperty.ComplexType.ClrType) + : new StructuralTypeReferenceExpression( + new RelationalStructuralTypeShaperExpression( + complexProperty.ComplexType, + nestedJsonQuery, + nestedJsonQuery.IsNullable)); + + default: + throw new UnreachableException(); + } case { Subquery: ShapedQueryExpression }: - throw new InvalidOperationException(); // TODO: Figure this out; do we support it? + throw new InvalidOperationException("Complex property binding over a subquery"); // TODO: #36296 default: throw new UnreachableException(); @@ -1951,6 +1976,11 @@ bool TryRewriteComplexTypeEquality([NotNullWhen(true)] out Expression? result) void GenerateComparisons(IComplexType type, Expression left, Expression right) { + if (type.IsMappedToJson()) + { + throw new NotImplementedException("Issue #36296"); + } + foreach (var property in type.GetProperties()) { var comparison = Infrastructure.ExpressionExtensions.CreateEqualsExpression( diff --git a/src/EFCore.Relational/Query/RelationalStructuralTypeShaperExpression.cs b/src/EFCore.Relational/Query/RelationalStructuralTypeShaperExpression.cs index 1ffb6609422..36f27101064 100644 --- a/src/EFCore.Relational/Query/RelationalStructuralTypeShaperExpression.cs +++ b/src/EFCore.Relational/Query/RelationalStructuralTypeShaperExpression.cs @@ -24,7 +24,7 @@ public class RelationalStructuralTypeShaperExpression : StructuralTypeShaperExpr /// An expression of ValueBuffer to get values for properties of the entity. /// A bool value indicating whether this entity instance can be null. public RelationalStructuralTypeShaperExpression(ITypeBase structuralType, Expression valueBufferExpression, bool nullable) - : base(structuralType, valueBufferExpression, nullable, null) + : base(structuralType, valueBufferExpression, nullable, materializationCondition: null) { } diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.Helper.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.Helper.cs index 94ab66a782c..d9a8555dcfa 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.Helper.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.Helper.cs @@ -278,9 +278,17 @@ protected override Expression VisitExtension(Expression expression) }; } - case CollectionResultExpression collectionResultExpression: + case CollectionResultExpression { + QueryExpression: ProjectionBindingExpression innerProjectionBindingExpression + } collectionResultExpression: { - var innerProjectionBindingExpression = collectionResultExpression.ProjectionBindingExpression; + var navigation = collectionResultExpression.Relationship switch + { + INavigationBase n => n, + null => null, + _ => throw new UnreachableException() + }; + var value = clientProjectionIndexMap[innerProjectionBindingExpression.Index!.Value]; return value switch { @@ -289,18 +297,18 @@ SingleCollectionInfo singleCollectionInfo singleCollectionInfo.ParentIdentifier, singleCollectionInfo.OuterIdentifier, singleCollectionInfo.SelfIdentifier, singleCollectionInfo.ParentIdentifierValueComparers, singleCollectionInfo.OuterIdentifierValueComparers, singleCollectionInfo.SelfIdentifierValueComparers, - singleCollectionInfo.ShaperExpression, collectionResultExpression.Navigation, + singleCollectionInfo.ShaperExpression, navigation, collectionResultExpression.ElementType), SplitCollectionInfo splitCollectionInfo => new RelationalSplitCollectionShaperExpression( splitCollectionInfo.ParentIdentifier, splitCollectionInfo.ChildIdentifier, splitCollectionInfo.IdentifierValueComparers, splitCollectionInfo.SelectExpression, - splitCollectionInfo.ShaperExpression, collectionResultExpression.Navigation, + splitCollectionInfo.ShaperExpression, navigation, collectionResultExpression.ElementType), int => collectionResultExpression.Update( - (ProjectionBindingExpression)Visit(collectionResultExpression.ProjectionBindingExpression)), + (ProjectionBindingExpression)Visit(innerProjectionBindingExpression)), _ => throw new InvalidOperationException() }; diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index a612221f6bb..aca5c888d99 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -350,21 +350,24 @@ void ProcessComplexType(StructuralTypeProjectionExpression complexTypeProjection foreach (var complexProperty in complexType.GetComplexProperties()) { - ProcessComplexType( - (StructuralTypeProjectionExpression)complexTypeProjection.BindComplexProperty(complexProperty) - .ValueBufferExpression); + if (!complexProperty.IsCollection) + { + var complexPropertyShaper = (StructuralTypeShaperExpression)complexTypeProjection.BindComplexProperty(complexProperty); + + ProcessComplexType((StructuralTypeProjectionExpression)complexPropertyShaper.ValueBufferExpression); + } } } break; - case JsonQueryExpression jsonQueryExpression: + case JsonQueryExpression { StructuralType: IEntityType entityType } jsonQueryExpression: if (jsonQueryExpression.IsCollection) { throw new InvalidOperationException(RelationalStrings.DistinctOnCollectionNotSupported); } - var primaryKeyProperties = jsonQueryExpression.EntityType.FindPrimaryKey()!.Properties; + var primaryKeyProperties = entityType.FindPrimaryKey()!.Properties; var primaryKeyPropertiesCount = jsonQueryExpression.IsCollection ? primaryKeyProperties.Count - 1 : primaryKeyProperties.Count; @@ -378,6 +381,9 @@ void ProcessComplexType(StructuralTypeProjectionExpression complexTypeProjection break; + case JsonQueryExpression { StructuralType: IComplexType complexType } jsonQueryExpression: + throw new NotImplementedException(); // #36296 + case SqlExpression sqlExpression: otherExpressions.Add(sqlExpression); break; @@ -485,8 +491,14 @@ void ProcessTypeProjection(StructuralTypeProjectionExpression projection) foreach (var complexProperty in GetAllComplexPropertiesInHierarchy(projection.StructuralType)) { - ProcessTypeProjection( - (StructuralTypeProjectionExpression)projection.BindComplexProperty(complexProperty).ValueBufferExpression); + if (complexProperty.IsCollection) + { + throw new NotImplementedException(); // #36296 + } + + var propertyShaper = (StructuralTypeShaperExpression)projection.BindComplexProperty(complexProperty); + + ProcessTypeProjection((StructuralTypeProjectionExpression)propertyShaper.ValueBufferExpression); } } @@ -1234,9 +1246,9 @@ Expression CopyProjectionToOuter(SelectExpression innerSelectExpression, Express ConstantExpression remappedConstant; switch (((ConstantExpression)innerSelectExpression._clientProjections[j]).Value!) { - case Dictionary entityDictionary: + case Dictionary entityDictionary: { - var newDictionary = new Dictionary(entityDictionary.Count); + var newDictionary = new Dictionary(entityDictionary.Count); foreach (var (property, value) in entityDictionary) { newDictionary[property] = projectionIndexMap[value]; @@ -1265,7 +1277,7 @@ Expression CopyProjectionToOuter(SelectExpression innerSelectExpression, Express case QueryableJsonProjectionInfo queryableJsonProjectionInfo: { - var newPropertyIndexMap = new Dictionary(queryableJsonProjectionInfo.PropertyIndexMap.Count); + var newPropertyIndexMap = new Dictionary(queryableJsonProjectionInfo.PropertyIndexMap.Count); foreach (var (property, value) in queryableJsonProjectionInfo.PropertyIndexMap) { newPropertyIndexMap[property] = projectionIndexMap[value]; @@ -1322,9 +1334,10 @@ Expression CopyProjectionToOuter(SelectExpression innerSelectExpression, Express { result[projectionMember] = expression switch { - StructuralTypeProjectionExpression projection => AddStructuralTypeProjection(projection), - JsonQueryExpression jsonQueryExpression => AddJsonProjection(jsonQueryExpression), - _ => Constant(AddToProjection((SqlExpression)expression, projectionMember.Last?.Name)) + StructuralTypeProjectionExpression p => AddStructuralTypeProjection(p), + JsonQueryExpression p => AddJsonProjection(p), + SqlExpression p => Constant(AddToProjection(p, projectionMember.Last?.Name)), + _ => throw new UnreachableException() }; } @@ -1349,7 +1362,7 @@ ConstantExpression AddStructuralTypeProjection(StructuralTypeProjectionExpressio if (projection is { StructuralType: IEntityType entityType } && entityType.IsMappedToJson()) { - var propertyIndexMap = new Dictionary(); + var propertyIndexMap = new Dictionary(); var ownerEntity = entityType; do @@ -1383,7 +1396,7 @@ ConstantExpression AddStructuralTypeProjection(StructuralTypeProjectionExpressio return Constant(new QueryableJsonProjectionInfo(propertyIndexMap, childrenProjectionInfo)); } - var projections = new Dictionary(); + var projections = new Dictionary(); ProcessType(projection); @@ -1403,8 +1416,25 @@ void ProcessType(StructuralTypeProjectionExpression typeProjection) foreach (var complexProperty in GetAllComplexPropertiesInHierarchy(typeProjection.StructuralType)) { - ProcessType( - (StructuralTypeProjectionExpression)typeProjection.BindComplexProperty(complexProperty).ValueBufferExpression); + switch (typeProjection.BindComplexProperty(complexProperty)) + { + case StructuralTypeShaperExpression { ValueBufferExpression: StructuralTypeProjectionExpression nestedProjection }: + ProcessType(nestedProjection); + break; + + // Complex (not owned) JSON, single (non-collection) + case StructuralTypeShaperExpression { ValueBufferExpression: JsonQueryExpression jsonQuery }: + projections[complexProperty] = ((JsonProjectionInfo)AddJsonProjection(jsonQuery).Value!).JsonColumnIndex; + break; + + // Complex (not owned) JSON, collection + case CollectionResultExpression { QueryExpression: JsonQueryExpression jsonQuery }: + projections[complexProperty] = ((JsonProjectionInfo)AddJsonProjection(jsonQuery).Value!).JsonColumnIndex; + break; + + default: + throw new UnreachableException(); + } } } @@ -1428,29 +1458,39 @@ ConstantExpression AddJsonProjection(JsonQueryExpression jsonQueryExpression) _projection.Add(new ProjectionExpression(jsonScalarExpression, "")); var jsonColumnIndex = _projection.Count - 1; var keyAccessInfo = new List<(IProperty?, int?, int?)>(); - var keyProperties = GetMappedKeyProperties(jsonQueryExpression.EntityType.FindPrimaryKey()!); - foreach (var keyProperty in keyProperties) - { - var keyColumn = jsonQueryExpression.BindProperty(keyProperty); - keyAccessInfo.Add((keyProperty, null, AddToProjection(keyColumn))); - } - foreach (var elementAccessSegment in jsonScalarExpression.Path.Where(x => x.ArrayIndex != null)) + switch (jsonQueryExpression.StructuralType) { - if (elementAccessSegment.ArrayIndex is SqlConstantExpression { Value: int intValue }) - { - keyAccessInfo.Add((null, intValue, null)); - } - else - { - keyAccessInfo.Add((null, null, AddToProjection(elementAccessSegment.ArrayIndex!))); - } + case IEntityType entityType: + var keyProperties = GetMappedKeyProperties(entityType.FindPrimaryKey()!); + foreach (var keyProperty in keyProperties) + { + var keyColumn = jsonQueryExpression.BindProperty(keyProperty); + keyAccessInfo.Add((keyProperty, null, AddToProjection(keyColumn))); + } + + foreach (var elementAccessSegment in jsonScalarExpression.Path.Where(x => x.ArrayIndex != null)) + { + if (elementAccessSegment.ArrayIndex is SqlConstantExpression { Value: int intValue }) + { + keyAccessInfo.Add((null, intValue, null)); + } + else + { + keyAccessInfo.Add((null, null, AddToProjection(elementAccessSegment.ArrayIndex!))); + } + } + break; + + case IComplexType complexType: + break; + + default: + throw new UnreachableException(); } - return Constant( - new JsonProjectionInfo( - jsonColumnIndex, - keyAccessInfo)); + + return Constant(new JsonProjectionInfo(jsonColumnIndex, keyAccessInfo)); } static IReadOnlyList GetMappedKeyProperties(IKey key) @@ -2231,7 +2271,7 @@ StructuralTypeProjectionExpression ProcessStructuralType( StructuralTypeProjectionExpression structuralProjection2) { var propertyExpressions = new Dictionary(); - var complexPropertyCache = new Dictionary(); + var complexPropertyCache = new Dictionary(); var type = structuralProjection1.StructuralType; foreach (var property in type.GetPropertiesInHierarchy()) @@ -2293,9 +2333,15 @@ StructuralTypeProjectionExpression ProcessStructuralType( var complexPropertyShaper1 = structuralProjection1.BindComplexProperty(complexProperty); var complexPropertyShaper2 = structuralProjection2.BindComplexProperty(complexProperty); + if (complexPropertyShaper1 is not StructuralTypeShaperExpression nonCollectionShaper1 + || complexPropertyShaper2 is not StructuralTypeShaperExpression nonCollectionShaper2) + { + throw new NotImplementedException("Set operation over collection complex properties"); + } + var resultComplexProjection = ProcessStructuralType( - (StructuralTypeProjectionExpression)complexPropertyShaper1.ValueBufferExpression, - (StructuralTypeProjectionExpression)complexPropertyShaper2.ValueBufferExpression); + (StructuralTypeProjectionExpression)nonCollectionShaper1.ValueBufferExpression, + (StructuralTypeProjectionExpression)nonCollectionShaper2.ValueBufferExpression); var resultComplexShaper = new RelationalStructuralTypeShaperExpression( complexProperty.ComplexType, @@ -2714,18 +2760,20 @@ static TableExpressionBase FindRootTableExpressionForColumn(SelectExpression sel /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - public static StructuralTypeShaperExpression GenerateComplexPropertyShaperExpression( + public static Expression GenerateComplexPropertyShaperExpression( StructuralTypeProjectionExpression containerProjection, IComplexProperty complexProperty) { + var complexType = complexProperty.ComplexType; var propertyExpressionMap = new Dictionary(); // We do not support complex type splitting, so we will only ever have a single table/view mapping to it. // See Issue #32853 and Issue #31248 - var complexTypeTable = complexProperty.ComplexType.GetViewOrTableMappings().Single().Table; + var complexTypeTable = complexType.GetViewOrTableMappings().Single().Table; + if (!containerProjection.TableMap.TryGetValue(complexTypeTable, out var tableAlias)) { - complexTypeTable = complexProperty.ComplexType.GetDefaultMappings().Single().Table; + complexTypeTable = complexType.GetDefaultMappings().Single().Table; tableAlias = containerProjection.TableMap[complexTypeTable]; } @@ -2740,7 +2788,47 @@ public static StructuralTypeShaperExpression GenerateComplexPropertyShaperExpres isComplexTypeNullable = true; } - foreach (var property in complexProperty.ComplexType.GetProperties()) + // The target type is a JSON complex type - generate a JsonQueryExpression. + if (complexType.IsMappedToJson()) + { + var containerColumnName = complexType.GetContainerColumnName(); + Check.DebugAssert(containerColumnName is not null, "Complex JSON type without a container column"); + var containerColumn = complexTypeTable.FindColumn(containerColumnName); + Check.DebugAssert(containerColumn is not null, "Complex JSON container table not found on relational table"); + + // If the source type is a JSON complex type; since we're binding over StructuralTypeProjectionExpression - which represents a relational + // table-like thing - this means that an internal JSON collection has been converted to a relational table (e.g. OPENJSON on SQL Server) + // and we're now binding over that table. + // Otherwise, if the source type isn't mapped to JSON, we're just binding to an actual JSON column in a relational table, and not within it. + var containerColumnExpression = complexProperty.DeclaringType.IsMappedToJson() + ? new ColumnExpression( + complexType.GetJsonPropertyName() ?? throw new UnreachableException($"No JSON property name for complex property {complexProperty.Name}"), + tableAlias, + complexProperty.ClrType, + typeMapping: containerColumn.StoreTypeMapping, + isComplexTypeNullable) + : new ColumnExpression( + containerColumn.Name, + tableAlias, + containerColumn, + complexProperty.ClrType, + containerColumn.StoreTypeMapping, + isComplexTypeNullable); + + var jsonQuery = new JsonQueryExpression( + complexType, + containerColumnExpression, + keyPropertyMap: null, + complexProperty.ClrType, + complexProperty.IsCollection); + + return complexProperty.IsCollection + ? new CollectionResultExpression(jsonQuery, complexProperty, elementType: complexType.ClrType) + : new RelationalStructuralTypeShaperExpression(complexType, jsonQuery, isComplexTypeNullable); + } + + // Table splitting + foreach (var property in complexType.GetProperties()) { // TODO: Reimplement EntityProjectionExpression via TableMap, and then use that here var column = complexTypeTable.FindColumn(property)!; @@ -2756,12 +2844,10 @@ public static StructuralTypeShaperExpression GenerateComplexPropertyShaperExpres Check.DebugAssert(newTableMap.Single().Key == complexTypeTable, "Bad new table map"); - var entityShaper = new RelationalStructuralTypeShaperExpression( - complexProperty.ComplexType, + return new RelationalStructuralTypeShaperExpression( + complexType, new StructuralTypeProjectionExpression(complexProperty.ComplexType, propertyExpressionMap, newTableMap, isComplexTypeNullable), isComplexTypeNullable); - - return entityShaper; } /// @@ -3602,7 +3688,7 @@ private SqlRemappingVisitor PushdownIntoSubqueryInternal(bool liftOrderings = tr if (item is StructuralTypeProjectionExpression projection) { - _clientProjections[i] = LiftEntityProjectionFromSubquery(projection, subqueryAlias); + _clientProjections[i] = LiftStructuralProjectionFromSubquery(projection, subqueryAlias); } else if (item is JsonQueryExpression jsonQueryExpression) { @@ -3635,7 +3721,7 @@ private SqlRemappingVisitor PushdownIntoSubqueryInternal(bool liftOrderings = tr switch (expression) { case StructuralTypeProjectionExpression projection: - _projectionMapping[projectionMember] = LiftEntityProjectionFromSubquery(projection, subqueryAlias); + _projectionMapping[projectionMember] = LiftStructuralProjectionFromSubquery(projection, subqueryAlias); break; case JsonQueryExpression jsonQueryExpression: @@ -3735,12 +3821,12 @@ private SqlRemappingVisitor PushdownIntoSubqueryInternal(bool liftOrderings = tr return sqlRemappingVisitor; - StructuralTypeProjectionExpression LiftEntityProjectionFromSubquery( + StructuralTypeProjectionExpression LiftStructuralProjectionFromSubquery( StructuralTypeProjectionExpression projection, string subqueryAlias) { var propertyExpressions = new Dictionary(); - var complexPropertyCache = new Dictionary(); + var complexPropertyCache = new Dictionary(); foreach (var property in projection.StructuralType.GetPropertiesInHierarchy()) { @@ -3762,9 +3848,14 @@ StructuralTypeProjectionExpression LiftEntityProjectionFromSubquery( foreach (var complexProperty in GetAllComplexPropertiesInHierarchy(projection.StructuralType)) { - var complexPropertyShaper = projection.BindComplexProperty(complexProperty); + if (complexProperty.IsCollection) + { + throw new NotImplementedException("#36296"); + } + + var complexPropertyShaper = (StructuralTypeShaperExpression)projection.BindComplexProperty(complexProperty); - var complexTypeProjectionExpression = LiftEntityProjectionFromSubquery( + var complexTypeProjectionExpression = LiftStructuralProjectionFromSubquery( (StructuralTypeProjectionExpression)complexPropertyShaper.ValueBufferExpression, subqueryAlias); @@ -3796,7 +3887,7 @@ StructuralTypeProjectionExpression LiftEntityProjectionFromSubquery( { var newValueBufferExpression = boundEntityShaperExpression.ValueBufferExpression is StructuralTypeProjectionExpression innerEntityProjection - ? (Expression)LiftEntityProjectionFromSubquery(innerEntityProjection, subqueryAlias) + ? (Expression)LiftStructuralProjectionFromSubquery(innerEntityProjection, subqueryAlias) : LiftJsonQueryFromSubquery((JsonQueryExpression)boundEntityShaperExpression.ValueBufferExpression); boundEntityShaperExpression = boundEntityShaperExpression.Update(newValueBufferExpression); @@ -3819,20 +3910,25 @@ JsonQueryExpression LiftJsonQueryFromSubquery(JsonQueryExpression jsonQueryExpre var newJsonColumn = subquery.GenerateOuterColumn(subqueryAlias, jsonScalarExpression); - var newKeyPropertyMap = new Dictionary(); - var keyProperties = jsonQueryExpression.KeyPropertyMap.Keys.ToList(); - for (var i = 0; i < keyProperties.Count; i++) + Dictionary? newKeyPropertyMap = null; + + if (jsonQueryExpression.KeyPropertyMap is not null) { - var keyProperty = keyProperties[i]; - var innerColumn = jsonQueryExpression.BindProperty(keyProperty); - var outerColumn = subquery.GenerateOuterColumn(subqueryAlias, innerColumn); - projectionMap[innerColumn] = outerColumn; - newKeyPropertyMap[keyProperty] = outerColumn; + newKeyPropertyMap = new Dictionary(); + var keyProperties = jsonQueryExpression.KeyPropertyMap.Keys.ToList(); + for (var i = 0; i < keyProperties.Count; i++) + { + var keyProperty = keyProperties[i]; + var innerColumn = jsonQueryExpression.BindProperty(keyProperty); + var outerColumn = subquery.GenerateOuterColumn(subqueryAlias, innerColumn); + projectionMap[innerColumn] = outerColumn; + newKeyPropertyMap[keyProperty] = outerColumn; + } } // clear up the json path - we start from empty path after pushdown return new JsonQueryExpression( - jsonQueryExpression.EntityType, + jsonQueryExpression.StructuralType, newJsonColumn, newKeyPropertyMap, jsonQueryExpression.Type, @@ -3857,7 +3953,7 @@ public bool IsNonComposedFromSql() pe => pe.Expression is ColumnExpression column && string.Equals(fromSql.Alias, column.TableAlias, StringComparison.OrdinalIgnoreCase)) && _projectionMapping.TryGetValue(new ProjectionMember(), out var mapping) - && mapping.Type == (fromSql.Table == null ? typeof(int) : typeof(Dictionary)); + && mapping.Type == (fromSql.Table == null ? typeof(int) : typeof(Dictionary)); /// /// Prepares the to apply aggregate operation over it. diff --git a/src/EFCore.Relational/Query/StructuralTypeProjectionExpression.cs b/src/EFCore.Relational/Query/StructuralTypeProjectionExpression.cs index 0a643036eab..8ca55f5227e 100644 --- a/src/EFCore.Relational/Query/StructuralTypeProjectionExpression.cs +++ b/src/EFCore.Relational/Query/StructuralTypeProjectionExpression.cs @@ -18,7 +18,7 @@ public class StructuralTypeProjectionExpression : Expression { private readonly IReadOnlyDictionary _propertyExpressionMap; private readonly Dictionary _ownedNavigationMap; - private Dictionary? _complexPropertyCache; + private Dictionary? _complexPropertyCache; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -54,7 +54,7 @@ public StructuralTypeProjectionExpression( public StructuralTypeProjectionExpression( ITypeBase type, IReadOnlyDictionary propertyExpressionMap, - Dictionary complexPropertyCache, + Dictionary complexPropertyCache, IReadOnlyDictionary tableMap, bool nullable = false, SqlExpression? discriminatorExpression = null) @@ -73,7 +73,7 @@ private StructuralTypeProjectionExpression( ITypeBase type, IReadOnlyDictionary propertyExpressionMap, Dictionary ownedNavigationMap, - Dictionary? complexPropertyCache, + Dictionary? complexPropertyCache, IReadOnlyDictionary tableMap, bool nullable, SqlExpression? discriminatorExpression = null) @@ -151,13 +151,13 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) propertyExpressionMap[property] = newExpression; } - var complexPropertyCache = default(Dictionary); + var complexPropertyCache = default(Dictionary); if (_complexPropertyCache != null) { - complexPropertyCache = new Dictionary(); + complexPropertyCache = new Dictionary(); foreach (var (complexProperty, complexShaper) in _complexPropertyCache) { - var newComplexShaper = (StructuralTypeShaperExpression)visitor.Visit(complexShaper); + var newComplexShaper = visitor.Visit(complexShaper); changed |= complexShaper != newComplexShaper; complexPropertyCache[complexProperty] = newComplexShaper; } @@ -193,13 +193,16 @@ public virtual StructuralTypeProjectionExpression MakeNullable() propertyExpressionMap[property] = columnExpression.MakeNullable(); } - var complexPropertyCache = default(Dictionary); + var complexPropertyCache = default(Dictionary); if (_complexPropertyCache != null) { - complexPropertyCache = new Dictionary(); + complexPropertyCache = new Dictionary(); foreach (var (complexProperty, complexShaper) in _complexPropertyCache) { - complexPropertyCache[complexProperty] = complexShaper.MakeNullable(); + if (complexShaper is StructuralTypeShaperExpression nonCollectionComplexShaper) + { + complexPropertyCache[complexProperty] = nonCollectionComplexShaper.MakeNullable(); + } } } @@ -264,10 +267,10 @@ public virtual StructuralTypeProjectionExpression UpdateEntityType(IEntityType d } } - var complexPropertyCache = default(Dictionary); + var complexPropertyCache = default(Dictionary); if (_complexPropertyCache != null) { - complexPropertyCache = new Dictionary(); + complexPropertyCache = new Dictionary(); foreach (var (complexProperty, complexShaper) in _complexPropertyCache) { if (derivedType.IsAssignableFrom(complexProperty.DeclaringType) @@ -367,11 +370,11 @@ public virtual ColumnExpression BindProperty(IProperty property) /// /// A complex property to bind. /// A shaper expression for the target complex type. - public virtual StructuralTypeShaperExpression BindComplexProperty(IComplexProperty complexProperty) + public virtual Expression BindComplexProperty(IComplexProperty complexProperty) { if (_complexPropertyCache is null || !_complexPropertyCache.TryGetValue(complexProperty, out var resultShaper)) { - _complexPropertyCache ??= new Dictionary(); + _complexPropertyCache ??= new Dictionary(); resultShaper = _complexPropertyCache[complexProperty] = SelectExpression.GenerateComplexPropertyShaperExpression(this, complexProperty); } diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs index e6de72208ea..a044008ceaa 100644 --- a/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs @@ -251,7 +251,7 @@ protected override ShapedQueryExpression TransformJsonQueryToTable(JsonQueryExpr var columnInfos = new List(); // We're only interested in properties which actually exist in the JSON, filter out uninteresting shadow keys - foreach (var property in jsonQueryExpression.EntityType.GetPropertiesInHierarchy()) + foreach (var property in jsonQueryExpression.StructuralType.GetPropertiesInHierarchy()) { if (property.GetJsonPropertyName() is string jsonPropertyName) { @@ -260,30 +260,51 @@ protected override ShapedQueryExpression TransformJsonQueryToTable(JsonQueryExpr { Name = jsonPropertyName, TypeMapping = property.GetRelationalTypeMapping(), - Path = new PathSegment[] { new(jsonPropertyName) }, + Path = [new(jsonPropertyName)], AsJson = property.GetRelationalTypeMapping().ElementTypeMapping is not null }); } } - // Navigations represent nested JSON owned entities, which we also add to the OPENJSON WITH clause, but with AS JSON. - foreach (var navigation in jsonQueryExpression.EntityType.GetNavigationsInHierarchy() - .Where( - n => n.ForeignKey.IsOwnership - && n.TargetEntityType.IsMappedToJson() - && n.ForeignKey.PrincipalToDependent == n)) + switch (jsonQueryExpression.StructuralType) { - var jsonNavigationName = navigation.TargetEntityType.GetJsonPropertyName(); - Check.DebugAssert(jsonNavigationName is not null, $"No JSON property name for navigation {navigation.Name}"); + case IEntityType entityType: + // Navigations represent nested JSON owned entities, which we also add to the OPENJSON WITH clause, but with AS JSON. + foreach (var navigation in entityType.GetNavigationsInHierarchy() + .Where( + n => n.ForeignKey.IsOwnership + && n.TargetEntityType.IsMappedToJson() + && n.ForeignKey.PrincipalToDependent == n)) + { + var jsonPropertyName = navigation.TargetEntityType.GetJsonPropertyName(); + Check.DebugAssert(jsonPropertyName is not null, $"No JSON property name for navigation {navigation.Name}"); + + AddStructuralColumnInfo(jsonPropertyName); + } + break; - columnInfos.Add( - new SqlServerOpenJsonExpression.ColumnInfo + case IComplexType complexType: + foreach (var complexProperty in complexType.GetComplexProperties()) { - Name = jsonNavigationName, - TypeMapping = _nvarcharMaxTypeMapping ??= _typeMappingSource.FindMapping("nvarchar(max)")!, - Path = new PathSegment[] { new(jsonNavigationName) }, - AsJson = true - }); + var jsonPropertyName = complexProperty.ComplexType.GetJsonPropertyName(); + Check.DebugAssert(jsonPropertyName is not null, $"No JSON property name for complex property {complexProperty.Name}"); + + AddStructuralColumnInfo(jsonPropertyName); + } + break; + + default: + throw new UnreachableException(); + + void AddStructuralColumnInfo(string jsonPropertyName) + => columnInfos.Add( + new SqlServerOpenJsonExpression.ColumnInfo + { + Name = jsonPropertyName, + TypeMapping = _nvarcharMaxTypeMapping ??= _typeMappingSource.FindMapping("nvarchar(max)")!, + Path = [new(jsonPropertyName)], + AsJson = true + }); } var openJsonExpression = new SqlServerOpenJsonExpression( @@ -315,7 +336,7 @@ protected override ShapedQueryExpression TransformJsonQueryToTable(JsonQueryExpr return new ShapedQueryExpression( selectExpression, new RelationalStructuralTypeShaperExpression( - jsonQueryExpression.EntityType, + jsonQueryExpression.StructuralType, new ProjectionBindingExpression( selectExpression, new ProjectionMember(), diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs index 4d73e72d2b6..972e6460e6f 100644 --- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryableMethodTranslatingExpressionVisitor.cs @@ -322,7 +322,7 @@ protected override QueryableMethodTranslatingExpressionVisitor CreateSubqueryVis /// protected override ShapedQueryExpression TransformJsonQueryToTable(JsonQueryExpression jsonQueryExpression) { - var entityType = jsonQueryExpression.EntityType; + var structuralType = jsonQueryExpression.StructuralType; var textTypeMapping = _typeMappingSource.FindMapping(typeof(string)); // TODO: Refactor this out @@ -375,14 +375,14 @@ protected override ShapedQueryExpression TransformJsonQueryToTable(JsonQueryExpr var jsonColumn = selectExpression.CreateColumnExpression( jsonEachExpression, JsonEachValueColumnName, typeof(string), _typeMappingSource.FindMapping(typeof(string))); // TODO: nullable? - var containerColumnName = entityType.GetContainerColumnName(); + var containerColumnName = structuralType.GetContainerColumnName(); Check.DebugAssert(containerColumnName is not null, "JsonQueryExpression to entity type without a container column name"); // First step: build a SelectExpression that will execute json_each and project all properties and navigations out, e.g. // (SELECT value ->> 'a' AS a, value ->> 'b' AS b FROM json_each(c."JsonColumn", '$.Something.SomeCollection') // We're only interested in properties which actually exist in the JSON, filter out uninteresting shadow keys - foreach (var property in entityType.GetPropertiesInHierarchy()) + foreach (var property in structuralType.GetPropertiesInHierarchy()) { if (property.GetJsonPropertyName() is string jsonPropertyName) { @@ -400,25 +400,30 @@ protected override ShapedQueryExpression TransformJsonQueryToTable(JsonQueryExpr } } - foreach (var navigation in jsonQueryExpression.EntityType.GetNavigationsInHierarchy() - .Where( - n => n.ForeignKey.IsOwnership - && n.TargetEntityType.IsMappedToJson() - && n.ForeignKey.PrincipalToDependent == n)) + if (jsonQueryExpression.StructuralType is IEntityType entityType) { - var jsonNavigationName = navigation.TargetEntityType.GetJsonPropertyName(); - Check.DebugAssert(jsonNavigationName is not null, "Invalid navigation found on JSON-mapped entity"); + foreach (var navigation in entityType.GetNavigationsInHierarchy() + .Where( + n => n.ForeignKey.IsOwnership + && n.TargetEntityType.IsMappedToJson() + && n.ForeignKey.PrincipalToDependent == n)) + { + var jsonNavigationName = navigation.TargetEntityType.GetJsonPropertyName(); + Check.DebugAssert(jsonNavigationName is not null, "Invalid navigation found on JSON-mapped entity"); - var projectionMember = new ProjectionMember().Append(new FakeMemberInfo(jsonNavigationName)); + var projectionMember = new ProjectionMember().Append(new FakeMemberInfo(jsonNavigationName)); - propertyJsonScalarExpression[projectionMember] = new JsonScalarExpression( - jsonColumn, - new[] { new PathSegment(jsonNavigationName) }, - typeof(string), - textTypeMapping, - !navigation.ForeignKey.IsRequiredDependent); + propertyJsonScalarExpression[projectionMember] = new JsonScalarExpression( + jsonColumn, + [new PathSegment(jsonNavigationName)], + typeof(string), + textTypeMapping, + !navigation.ForeignKey.IsRequiredDependent); + } } + // TODO: also add JsonScalarExpressions for complex properties, not just owned entities. #36296. + selectExpression.ReplaceProjection(propertyJsonScalarExpression); // Second step: push the above SelectExpression down to a subquery, and project an entity projection from the outer @@ -451,7 +456,7 @@ protected override ShapedQueryExpression TransformJsonQueryToTable(JsonQueryExpr return new ShapedQueryExpression( newOuterSelectExpression, new RelationalStructuralTypeShaperExpression( - jsonQueryExpression.EntityType, + jsonQueryExpression.StructuralType, new ProjectionBindingExpression( newOuterSelectExpression, new ProjectionMember(), diff --git a/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs b/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs index ba3e6328170..3c388b510df 100644 --- a/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs +++ b/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs @@ -417,7 +417,7 @@ private void SetValue(int index, object? value) _values[index] = value; } - private IEntityMaterializerSource MaterializerSource + private IStructuralTypeMaterializerSource MaterializerSource => InternalEntry.StateManager.EntityMaterializerSource; /// diff --git a/src/EFCore/ChangeTracking/Internal/EntryPropertyValues.cs b/src/EFCore/ChangeTracking/Internal/EntryPropertyValues.cs index f891e6f3da7..0c1d989c68d 100644 --- a/src/EFCore/ChangeTracking/Internal/EntryPropertyValues.cs +++ b/src/EFCore/ChangeTracking/Internal/EntryPropertyValues.cs @@ -395,7 +395,7 @@ private void SetValuesFromDictionary(InternalEntryBase entry, IRuntim } } - private IEntityMaterializerSource MaterializerSource + private IStructuralTypeMaterializerSource MaterializerSource => InternalEntry.StateManager.EntityMaterializerSource; /// diff --git a/src/EFCore/ChangeTracking/Internal/IStateManager.cs b/src/EFCore/ChangeTracking/Internal/IStateManager.cs index 6cca2cb6845..36f6fe46cc4 100644 --- a/src/EFCore/ChangeTracking/Internal/IStateManager.cs +++ b/src/EFCore/ChangeTracking/Internal/IStateManager.cs @@ -239,7 +239,7 @@ IEnumerable GetNonDeletedEntities() /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - IEntityMaterializerSource EntityMaterializerSource { get; } + IStructuralTypeMaterializerSource EntityMaterializerSource { get; } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs index 5eda78d7a6c..411e875575a 100644 --- a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs +++ b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs @@ -62,7 +62,7 @@ public InternalEntityEntry( IStateManager stateManager, IEntityType entityType, IDictionary values, - IEntityMaterializerSource entityMaterializerSource) + IStructuralTypeMaterializerSource entityMaterializerSource) : base((IRuntimeTypeBase)entityType, values) { StateManager = stateManager; diff --git a/src/EFCore/ChangeTracking/Internal/NavigationFixer.cs b/src/EFCore/ChangeTracking/Internal/NavigationFixer.cs index d9f4a9e171f..e5e1ebac0ef 100644 --- a/src/EFCore/ChangeTracking/Internal/NavigationFixer.cs +++ b/src/EFCore/ChangeTracking/Internal/NavigationFixer.cs @@ -22,7 +22,7 @@ public class NavigationFixer : INavigationFixer bool SetModified)>? _danglingJoinEntities; private readonly IEntityGraphAttacher _attacher; - private readonly IEntityMaterializerSource _entityMaterializerSource; + private readonly IStructuralTypeMaterializerSource _entityMaterializerSource; private bool _inFixup; private bool _inAttachGraph; @@ -34,7 +34,7 @@ public class NavigationFixer : INavigationFixer /// public NavigationFixer( IEntityGraphAttacher attacher, - IEntityMaterializerSource entityMaterializerSource) + IStructuralTypeMaterializerSource entityMaterializerSource) { _attacher = attacher; _entityMaterializerSource = entityMaterializerSource; diff --git a/src/EFCore/ChangeTracking/Internal/StateManager.cs b/src/EFCore/ChangeTracking/Internal/StateManager.cs index da6969ebd80..5527fc6e1e0 100644 --- a/src/EFCore/ChangeTracking/Internal/StateManager.cs +++ b/src/EFCore/ChangeTracking/Internal/StateManager.cs @@ -174,7 +174,7 @@ public virtual IModel Model /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public virtual IEntityMaterializerSource EntityMaterializerSource { get; } + public virtual IStructuralTypeMaterializerSource EntityMaterializerSource { get; } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore/ChangeTracking/Internal/StateManagerDependencies.cs b/src/EFCore/ChangeTracking/Internal/StateManagerDependencies.cs index 3e3359df8b7..2fda9d6ced1 100644 --- a/src/EFCore/ChangeTracking/Internal/StateManagerDependencies.cs +++ b/src/EFCore/ChangeTracking/Internal/StateManagerDependencies.cs @@ -57,7 +57,7 @@ public StateManagerDependencies( ICurrentDbContext currentContext, IEntityFinderSource entityFinderSource, IDbSetSource setSource, - IEntityMaterializerSource entityMaterializerSource, + IStructuralTypeMaterializerSource entityMaterializerSource, IExecutionStrategy executionStrategy, ICoreSingletonOptions coreSingletonOptions, ILoggingOptions loggingOptions, @@ -165,7 +165,7 @@ public StateManagerDependencies( /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public IEntityMaterializerSource EntityMaterializerSource { get; init; } + public IStructuralTypeMaterializerSource EntityMaterializerSource { get; init; } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs b/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs index 28a4deef4f3..b9d52303691 100644 --- a/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs +++ b/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs @@ -60,7 +60,7 @@ public static readonly IDictionary CoreServices { typeof(IDbSetInitializer), new ServiceCharacteristics(ServiceLifetime.Singleton) }, { typeof(IDbSetSource), new ServiceCharacteristics(ServiceLifetime.Singleton) }, { typeof(IEntityFinderSource), new ServiceCharacteristics(ServiceLifetime.Singleton) }, - { typeof(IEntityMaterializerSource), new ServiceCharacteristics(ServiceLifetime.Singleton) }, + { typeof(IStructuralTypeMaterializerSource), new ServiceCharacteristics(ServiceLifetime.Singleton) }, { typeof(ITypeMappingSource), new ServiceCharacteristics(ServiceLifetime.Singleton) }, { typeof(IModelCustomizer), new ServiceCharacteristics(ServiceLifetime.Singleton) }, { typeof(IModelCacheKeyFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) }, @@ -240,7 +240,7 @@ public virtual EntityFrameworkServicesBuilder TryAddCoreServices() TryAdd(); TryAdd(); TryAdd(); - TryAdd(); + TryAdd(); TryAdd(); TryAdd(); TryAdd(); @@ -330,7 +330,7 @@ public virtual EntityFrameworkServicesBuilder TryAddCoreServices() .AddDependencySingleton() .AddDependencySingleton() .AddDependencySingleton() - .AddDependencySingleton() + .AddDependencySingleton() .AddDependencySingleton() .AddDependencySingleton() .AddDependencySingleton() diff --git a/src/EFCore/Metadata/INavigationBase.cs b/src/EFCore/Metadata/INavigationBase.cs index 116c68cf601..c28a1222124 100644 --- a/src/EFCore/Metadata/INavigationBase.cs +++ b/src/EFCore/Metadata/INavigationBase.cs @@ -38,13 +38,6 @@ public interface INavigationBase : IReadOnlyNavigationBase, IPropertyBase get => (INavigationBase?)((IReadOnlyNavigationBase)this).Inverse; } - /// - /// Gets the for this navigation property, if it's a collection - /// navigation. - /// - /// The accessor. - IClrCollectionAccessor? GetCollectionAccessor(); - /// /// Calls for a to mark it as loaded /// when a no-tracking query has eagerly loaded this relationship. diff --git a/src/EFCore/Metadata/IPropertyBase.cs b/src/EFCore/Metadata/IPropertyBase.cs index c477ec7be12..a676e7eac12 100644 --- a/src/EFCore/Metadata/IPropertyBase.cs +++ b/src/EFCore/Metadata/IPropertyBase.cs @@ -65,4 +65,10 @@ int GetIndex() /// /// The comparer. IComparer GetCurrentValueComparer(); + + /// + /// Gets the for this property, if it's a collection navigation or complex property. + /// + /// The accessor. + IClrCollectionAccessor? GetCollectionAccessor() => null; } diff --git a/src/EFCore/Metadata/IReadOnlyTypeBase.cs b/src/EFCore/Metadata/IReadOnlyTypeBase.cs index 595b3affd6c..f5d04a545d7 100644 --- a/src/EFCore/Metadata/IReadOnlyTypeBase.cs +++ b/src/EFCore/Metadata/IReadOnlyTypeBase.cs @@ -461,7 +461,7 @@ IReadOnlyProperty GetProperty(string name) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - Func GetOrCreateMaterializer(IEntityMaterializerSource source); + Func GetOrCreateMaterializer(IStructuralTypeMaterializerSource source); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -470,5 +470,5 @@ IReadOnlyProperty GetProperty(string name) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - Func GetOrCreateEmptyMaterializer(IEntityMaterializerSource source); + Func GetOrCreateEmptyMaterializer(IStructuralTypeMaterializerSource source); } diff --git a/src/EFCore/Metadata/Internal/ClrCollectionAccessorFactory.cs b/src/EFCore/Metadata/Internal/ClrCollectionAccessorFactory.cs index 4526ba236df..5a56de2633c 100644 --- a/src/EFCore/Metadata/Internal/ClrCollectionAccessorFactory.cs +++ b/src/EFCore/Metadata/Internal/ClrCollectionAccessorFactory.cs @@ -44,37 +44,44 @@ private ClrCollectionAccessorFactory() /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public virtual IClrCollectionAccessor? Create(INavigationBase navigation) + public virtual IClrCollectionAccessor? Create(IPropertyBase relationship) { - if (!navigation.IsCollection) + if (!relationship.IsCollection) { return null; } // ReSharper disable once SuspiciousTypeConversion.Global - if (navigation is IClrCollectionAccessor accessor) + if (relationship is IClrCollectionAccessor accessor) { return accessor; } - var targetType = navigation.TargetEntityType; + var targetType = relationship switch + { + INavigationBase navigation => (ITypeBase)navigation.TargetEntityType, + IComplexProperty complexProperty => complexProperty.ComplexType, + + _ => throw new UnreachableException() + }; + if (targetType == null) { return null; } - var memberInfo = GetMostDerivedMemberInfo(navigation); - var propertyType = navigation.IsIndexerProperty() || navigation.IsShadowProperty() - ? navigation.ClrType + var memberInfo = GetMostDerivedMemberInfo(relationship); + var propertyType = relationship.IsIndexerProperty() || relationship.IsShadowProperty() + ? relationship.ClrType : memberInfo!.GetMemberType(); var elementType = propertyType.TryGetElementType(typeof(IEnumerable<>)); if (elementType == null) { throw new InvalidOperationException( - CoreStrings.NavigationBadType( - navigation.Name, - navigation.DeclaringType.DisplayName(), + CoreStrings.NavigationBadType( // TODO: Update + relationship.Name, + relationship.DeclaringType.DisplayName(), propertyType.ShortDisplayName(), targetType.DisplayName())); } @@ -83,17 +90,17 @@ private ClrCollectionAccessorFactory() { throw new InvalidOperationException( CoreStrings.NavigationArray( - navigation.Name, - navigation.DeclaringType.DisplayName(), + relationship.Name, + relationship.DeclaringType.DisplayName(), propertyType.ShortDisplayName())); } var boundMethod = GenericCreate.MakeGenericMethod( - memberInfo?.DeclaringType ?? navigation.DeclaringType.ClrType, propertyType, elementType); + memberInfo?.DeclaringType ?? relationship.DeclaringType.ClrType, propertyType, elementType); try { - return (IClrCollectionAccessor?)boundMethod.Invoke(null, [navigation]); + return (IClrCollectionAccessor?)boundMethod.Invoke(null, [relationship]); } catch (TargetInvocationException invocationException) { @@ -102,13 +109,13 @@ private ClrCollectionAccessorFactory() } [UsedImplicitly] - private static IClrCollectionAccessor CreateGeneric(INavigationBase navigation) + private static IClrCollectionAccessor CreateGeneric(IPropertyBase relationship) where TEntity : class where TCollection : class, IEnumerable where TElement : class { CreateExpressions( - navigation, + relationship, out var getCollection, out var setCollection, out var setCollectionForMaterialization, @@ -116,8 +123,8 @@ private static IClrCollectionAccessor CreateGeneric( - navigation.Name, - navigation.IsShadowProperty(), + relationship.Name, + relationship.IsShadowProperty(), getCollection?.Compile(), setCollection?.Compile(), setCollectionForMaterialization?.Compile(), @@ -174,7 +181,7 @@ private static readonly MethodInfo GenericCreateExpressions [UsedImplicitly] private static void CreateExpressions( - INavigationBase navigation, + IPropertyBase relationship, out Expression>? getCollection, out Expression>? setCollection, out Expression>? setCollectionForMaterialization, @@ -193,11 +200,11 @@ private static void CreateExpressions( var entityParameter = Expression.Parameter(typeof(TEntity), "entity"); var valueParameter = Expression.Parameter(typeof(TCollection), "collection"); - if (!navigation.IsShadowProperty()) + if (!relationship.IsShadowProperty()) { - var memberInfoForRead = navigation.GetMemberInfo(forMaterialization: false, forSet: false); - navigation.TryGetMemberInfo(forMaterialization: false, forSet: true, out var memberInfoForWrite, out _); - navigation.TryGetMemberInfo(forMaterialization: true, forSet: true, out var memberInfoForMaterialization, out _); + var memberInfoForRead = relationship.GetMemberInfo(forMaterialization: false, forSet: false); + relationship.TryGetMemberInfo(forMaterialization: false, forSet: true, out var memberInfoForWrite, out _); + relationship.TryGetMemberInfo(forMaterialization: true, forSet: true, out var memberInfoForMaterialization, out _); var memberAccessForRead = (Expression)Expression.MakeMemberAccess(entityParameter, memberInfoForRead); if (memberAccessForRead.Type != typeof(TCollection)) { @@ -222,7 +229,7 @@ private static void CreateExpressions( var concreteType = CollectionTypeFactory.Instance.TryFindTypeToInstantiate( typeof(TEntity), typeof(TCollection), - navigation.DeclaringEntityType.Model[CoreAnnotationNames.FullChangeTrackingNotificationsRequired] != null); + relationship.DeclaringType.Model[CoreAnnotationNames.FullChangeTrackingNotificationsRequired] != null); if (concreteType != null) { var isHashSet = concreteType.IsGenericType && concreteType.GetGenericTypeDefinition() == typeof(HashSet<>); @@ -268,10 +275,10 @@ static Expression> CreateSetterDelegate( valueParameter1); } - private static MemberInfo? GetMostDerivedMemberInfo(INavigationBase navigation) + private static MemberInfo? GetMostDerivedMemberInfo(IPropertyBase relationship) { - var propertyInfo = navigation.PropertyInfo; - var fieldInfo = navigation.FieldInfo; + var propertyInfo = relationship.PropertyInfo; + var fieldInfo = relationship.FieldInfo; return fieldInfo == null ? propertyInfo diff --git a/src/EFCore/Metadata/Internal/ComplexProperty.cs b/src/EFCore/Metadata/Internal/ComplexProperty.cs index e8fbeaa9d2c..e458b1f627d 100644 --- a/src/EFCore/Metadata/Internal/ComplexProperty.cs +++ b/src/EFCore/Metadata/Internal/ComplexProperty.cs @@ -17,6 +17,10 @@ public class ComplexProperty : PropertyBase, IMutableComplexProperty, IConventio private InternalComplexPropertyBuilder? _builder; private bool? _isNullable; + // Warning: Never access these fields directly as access needs to be thread-safe + private IClrCollectionAccessor? _collectionAccessor; + private bool _collectionAccessorInitialized; + private ConfigurationSource? _isNullableConfigurationSource; /// @@ -252,6 +256,19 @@ public static bool IsCompatible( return true; } + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual IClrCollectionAccessor? CollectionAccessor + => NonCapturingLazyInitializer.EnsureInitialized( + ref _collectionAccessor, + ref _collectionAccessorInitialized, + this, + static complexProperty => ClrCollectionAccessorFactory.Instance.Create(complexProperty)); + /// /// Runs the conventions when an annotation was set or removed. /// @@ -354,4 +371,13 @@ IComplexType IComplexProperty.ComplexType bool? IConventionComplexProperty.SetIsNullable(bool? nullable, bool fromDataAnnotation) => SetIsNullable( nullable, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + IClrCollectionAccessor? IPropertyBase.GetCollectionAccessor() + => CollectionAccessor; } diff --git a/src/EFCore/Metadata/Internal/ComplexType.cs b/src/EFCore/Metadata/Internal/ComplexType.cs index 1f79a31dac4..b3929de7122 100644 --- a/src/EFCore/Metadata/Internal/ComplexType.cs +++ b/src/EFCore/Metadata/Internal/ComplexType.cs @@ -413,7 +413,7 @@ public override PropertyCounts CalculateCounts() /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - public override Func GetOrCreateMaterializer(IEntityMaterializerSource source) + public override Func GetOrCreateMaterializer(IStructuralTypeMaterializerSource source) => source.GetMaterializer(this); /// @@ -423,7 +423,7 @@ public override Func GetOrCreateMaterializer(IEn /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - public override Func GetOrCreateEmptyMaterializer(IEntityMaterializerSource source) + public override Func GetOrCreateEmptyMaterializer(IStructuralTypeMaterializerSource source) => source.GetEmptyMaterializer(this); /// diff --git a/src/EFCore/Metadata/Internal/EntityType.cs b/src/EFCore/Metadata/Internal/EntityType.cs index 6dc13fe7076..166d152c306 100644 --- a/src/EFCore/Metadata/Internal/EntityType.cs +++ b/src/EFCore/Metadata/Internal/EntityType.cs @@ -2892,7 +2892,7 @@ public virtual bool IsImplicitlyCreatedJoinEntityType /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - public override Func GetOrCreateMaterializer(IEntityMaterializerSource source) + public override Func GetOrCreateMaterializer(IStructuralTypeMaterializerSource source) => source.GetMaterializer(this); /// @@ -2902,7 +2902,7 @@ public override Func GetOrCreateMaterializer(IEn /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - public override Func GetOrCreateEmptyMaterializer(IEntityMaterializerSource source) + public override Func GetOrCreateEmptyMaterializer(IStructuralTypeMaterializerSource source) => source.GetEmptyMaterializer(this); #endregion diff --git a/src/EFCore/Metadata/Internal/Navigation.cs b/src/EFCore/Metadata/Internal/Navigation.cs index 050a0ce2b04..46624377320 100644 --- a/src/EFCore/Metadata/Internal/Navigation.cs +++ b/src/EFCore/Metadata/Internal/Navigation.cs @@ -401,6 +401,6 @@ IConventionAnnotatableBuilder IConventionAnnotatable.Builder /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - IClrCollectionAccessor? INavigationBase.GetCollectionAccessor() + IClrCollectionAccessor? IPropertyBase.GetCollectionAccessor() => CollectionAccessor; } diff --git a/src/EFCore/Metadata/Internal/SkipNavigation.cs b/src/EFCore/Metadata/Internal/SkipNavigation.cs index d0feefdae53..68376547ffa 100644 --- a/src/EFCore/Metadata/Internal/SkipNavigation.cs +++ b/src/EFCore/Metadata/Internal/SkipNavigation.cs @@ -472,7 +472,7 @@ IReadOnlySkipNavigation IReadOnlySkipNavigation.Inverse /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - IClrCollectionAccessor? INavigationBase.GetCollectionAccessor() + IClrCollectionAccessor? IPropertyBase.GetCollectionAccessor() => CollectionAccessor; /// diff --git a/src/EFCore/Metadata/Internal/TypeBase.cs b/src/EFCore/Metadata/Internal/TypeBase.cs index 121856defd9..1dda9946e09 100644 --- a/src/EFCore/Metadata/Internal/TypeBase.cs +++ b/src/EFCore/Metadata/Internal/TypeBase.cs @@ -1715,7 +1715,7 @@ public virtual IEnumerable GetSnapshottableMembers() /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - public abstract Func GetOrCreateMaterializer(IEntityMaterializerSource source); + public abstract Func GetOrCreateMaterializer(IStructuralTypeMaterializerSource source); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1724,7 +1724,7 @@ public virtual IEnumerable GetSnapshottableMembers() /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - public abstract Func GetOrCreateEmptyMaterializer(IEntityMaterializerSource source); + public abstract Func GetOrCreateEmptyMaterializer(IStructuralTypeMaterializerSource source); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore/Metadata/ParameterBindingInfo.cs b/src/EFCore/Metadata/ParameterBindingInfo.cs index 9b25c1f3ada..e643bee5cc5 100644 --- a/src/EFCore/Metadata/ParameterBindingInfo.cs +++ b/src/EFCore/Metadata/ParameterBindingInfo.cs @@ -34,7 +34,7 @@ public ParameterBindingInfo( /// Parameters for the materialization that is happening. /// The expression tree from which the parameter value will come. public ParameterBindingInfo( - EntityMaterializerSourceParameters materializerSourceParameters, + StructuralTypeMaterializerSourceParameters materializerSourceParameters, Expression materializationContextExpression) { StructuralType = materializerSourceParameters.StructuralType; diff --git a/src/EFCore/Metadata/RuntimeComplexProperty.cs b/src/EFCore/Metadata/RuntimeComplexProperty.cs index 3ef1dd1b00c..bd1e828f530 100644 --- a/src/EFCore/Metadata/RuntimeComplexProperty.cs +++ b/src/EFCore/Metadata/RuntimeComplexProperty.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata.Internal; namespace Microsoft.EntityFrameworkCore.Metadata; @@ -16,6 +18,10 @@ public class RuntimeComplexProperty : RuntimePropertyBase, IRuntimeComplexProper { private readonly bool _isNullable; + // Warning: Never access these fields directly as access needs to be thread-safe + private IClrCollectionAccessor? _collectionAccessor; + private bool _collectionAccessorInitialized; + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -131,4 +137,17 @@ bool IReadOnlyComplexProperty.IsNullable [DebuggerStepThrough] get => _isNullable; } + + /// + [DebuggerStepThrough] + IClrCollectionAccessor? IPropertyBase.GetCollectionAccessor() + => NonCapturingLazyInitializer.EnsureInitialized( + ref _collectionAccessor, + ref _collectionAccessorInitialized, + this, + static complexProperty => ((IComplexProperty)complexProperty).IsCollection + ? RuntimeFeature.IsDynamicCodeSupported + ? ClrCollectionAccessorFactory.Instance.Create(complexProperty) + : throw new InvalidOperationException(CoreStrings.NativeAotNoCompiledModel) + : null); } diff --git a/src/EFCore/Metadata/RuntimeComplexType.cs b/src/EFCore/Metadata/RuntimeComplexType.cs index b0f5e2c1fda..f14b6d72d00 100644 --- a/src/EFCore/Metadata/RuntimeComplexType.cs +++ b/src/EFCore/Metadata/RuntimeComplexType.cs @@ -187,7 +187,7 @@ public override PropertyCounts CalculateCounts() /// // TODO: Make this part of the compiled model to make it work with NativeAOT, Issue #32923 [EntityFrameworkInternal] - public override Func GetOrCreateMaterializer(IEntityMaterializerSource source) + public override Func GetOrCreateMaterializer(IStructuralTypeMaterializerSource source) => NonCapturingLazyInitializer.EnsureInitialized( ref _materializer, this, source, static (e, s) => s.GetMaterializer(e)); @@ -199,7 +199,7 @@ public override Func GetOrCreateMaterializer(IEn /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - public override Func GetOrCreateEmptyMaterializer(IEntityMaterializerSource source) + public override Func GetOrCreateEmptyMaterializer(IStructuralTypeMaterializerSource source) => NonCapturingLazyInitializer.EnsureInitialized( ref _emptyMaterializer, this, source, static (e, s) => s.GetEmptyMaterializer(e)); diff --git a/src/EFCore/Metadata/RuntimeEntityType.cs b/src/EFCore/Metadata/RuntimeEntityType.cs index 052e5af3be1..a605372373c 100644 --- a/src/EFCore/Metadata/RuntimeEntityType.cs +++ b/src/EFCore/Metadata/RuntimeEntityType.cs @@ -875,7 +875,7 @@ public override IEnumerable GetSnapshottableMembers() /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - public override Func GetOrCreateMaterializer(IEntityMaterializerSource source) + public override Func GetOrCreateMaterializer(IStructuralTypeMaterializerSource source) => NonCapturingLazyInitializer.EnsureInitialized( ref _materializer, this, source, static (e, s) => s.GetMaterializer(e)); @@ -887,7 +887,7 @@ public override Func GetOrCreateMaterializer(IEn /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - public override Func GetOrCreateEmptyMaterializer(IEntityMaterializerSource source) + public override Func GetOrCreateEmptyMaterializer(IStructuralTypeMaterializerSource source) => NonCapturingLazyInitializer.EnsureInitialized( ref _emptyMaterializer, this, source, static (e, s) => s.GetEmptyMaterializer(e)); diff --git a/src/EFCore/Metadata/RuntimeNavigation.cs b/src/EFCore/Metadata/RuntimeNavigation.cs index 091c0837a47..a96ca571849 100644 --- a/src/EFCore/Metadata/RuntimeNavigation.cs +++ b/src/EFCore/Metadata/RuntimeNavigation.cs @@ -143,7 +143,7 @@ IReadOnlyForeignKey IReadOnlyNavigation.ForeignKey /// [DebuggerStepThrough] - IClrCollectionAccessor? INavigationBase.GetCollectionAccessor() + IClrCollectionAccessor? IPropertyBase.GetCollectionAccessor() => NonCapturingLazyInitializer.EnsureInitialized( ref _collectionAccessor, ref _collectionAccessorInitialized, diff --git a/src/EFCore/Metadata/RuntimeSkipNavigation.cs b/src/EFCore/Metadata/RuntimeSkipNavigation.cs index 4db68f7885a..5419c87282e 100644 --- a/src/EFCore/Metadata/RuntimeSkipNavigation.cs +++ b/src/EFCore/Metadata/RuntimeSkipNavigation.cs @@ -192,7 +192,7 @@ bool IReadOnlySkipNavigation.IsOnDependent } /// - IClrCollectionAccessor? INavigationBase.GetCollectionAccessor() + IClrCollectionAccessor? IPropertyBase.GetCollectionAccessor() => NonCapturingLazyInitializer.EnsureInitialized( ref _collectionAccessor, ref _collectionAccessorInitialized, diff --git a/src/EFCore/Metadata/RuntimeTypeBase.cs b/src/EFCore/Metadata/RuntimeTypeBase.cs index d680163e831..3e8d9c1dc42 100644 --- a/src/EFCore/Metadata/RuntimeTypeBase.cs +++ b/src/EFCore/Metadata/RuntimeTypeBase.cs @@ -798,7 +798,7 @@ public Func EmptyShadowValuesFactory /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - public abstract Func GetOrCreateMaterializer(IEntityMaterializerSource source); + public abstract Func GetOrCreateMaterializer(IStructuralTypeMaterializerSource source); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -807,7 +807,7 @@ public Func EmptyShadowValuesFactory /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - public abstract Func GetOrCreateEmptyMaterializer(IEntityMaterializerSource source); + public abstract Func GetOrCreateEmptyMaterializer(IStructuralTypeMaterializerSource source); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore/Query/EntityMaterializerSourceParameters.cs b/src/EFCore/Query/EntityMaterializerSourceParameters.cs index 4ff956748c7..88677025bbf 100644 --- a/src/EFCore/Query/EntityMaterializerSourceParameters.cs +++ b/src/EFCore/Query/EntityMaterializerSourceParameters.cs @@ -4,13 +4,22 @@ namespace Microsoft.EntityFrameworkCore.Query; /// -/// Parameter object for . +/// Parameter object for . /// /// The entity or complex type being materialized. /// The name of the instance being materialized. /// /// The query tracking behavior, or if this materialization is not from a query. /// +public readonly record struct StructuralTypeMaterializerSourceParameters( + ITypeBase StructuralType, + string InstanceName, + QueryTrackingBehavior? QueryTrackingBehavior); + +/// +/// This type has been obsoleted, use instead. +/// +[Obsolete("This type has been obsoleted, use StructuralTypeMaterializerSourceParameters instead.", error: true)] public readonly record struct EntityMaterializerSourceParameters( ITypeBase StructuralType, string InstanceName, diff --git a/src/EFCore/Query/IEntityMaterializerSource.cs b/src/EFCore/Query/IStructuralTypeMaterializerSource.cs similarity index 72% rename from src/EFCore/Query/IEntityMaterializerSource.cs rename to src/EFCore/Query/IStructuralTypeMaterializerSource.cs index 921a5f2836a..770d03aa307 100644 --- a/src/EFCore/Query/IEntityMaterializerSource.cs +++ b/src/EFCore/Query/IStructuralTypeMaterializerSource.cs @@ -6,7 +6,7 @@ namespace Microsoft.EntityFrameworkCore.Query; /// /// /// Defines a source for generating trees that read values from -/// a or creates entity instances. +/// a or creates structural type instances. /// /// /// This type is typically used by database providers (and other extensions). It is generally @@ -24,27 +24,8 @@ namespace Microsoft.EntityFrameworkCore.Query; /// and How EF Core queries work for more information and examples. /// /// -public interface IEntityMaterializerSource +public interface IStructuralTypeMaterializerSource { - /// - /// - /// Creates an tree representing creating an entity instance. - /// - /// - /// This method is typically used by database providers (and other extensions). It is generally - /// not used in application code. - /// - /// - /// The entity type being materialized. - /// The name of the instance being materialized. - /// The materialization expression to build on. - /// An expression to read the value. - [Obsolete("Use the overload that accepts an EntityMaterializerSourceParameters object.")] - Expression CreateMaterializeExpression( - IEntityType entityType, - string entityInstanceName, - Expression materializationExpression); - /// /// /// Creates an tree representing creating an entity instance. @@ -57,14 +38,7 @@ Expression CreateMaterializeExpression( /// Parameters for the entity being materialized. /// The materialization expression to build on. /// An expression to read the value. -#pragma warning disable CS0618 - Expression CreateMaterializeExpression( - EntityMaterializerSourceParameters parameters, - Expression materializationExpression) - => parameters.StructuralType is IEntityType entityType - ? CreateMaterializeExpression(entityType, parameters.InstanceName, materializationExpression) - : throw new NotImplementedException(CoreStrings.ComplexTypesNotSupported(GetType().Name)); -#pragma warning restore CS0618 + Expression CreateMaterializeExpression(StructuralTypeMaterializerSourceParameters parameters, Expression materializationExpression); /// /// @@ -118,3 +92,10 @@ Expression CreateMaterializeExpression( /// A delegate to create instances. Func GetEmptyMaterializer(IComplexType complexType); } + +/// +/// This interface has been obsoleted, use instead. +/// +[Obsolete("This interface has been obsoleted, use IEntityMaterializerSource instead.", error: true)] +public interface IEntityMaterializerSource; + diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs index 707fd2523e3..80dceffb0a7 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs @@ -80,7 +80,8 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp } var innerExpression = root.UnwrapTypeConversion(out var convertedType); - if (UnwrapEntityReference(innerExpression) is EntityReference entityReference) + var entityReference = UnwrapEntityReference(innerExpression); + if (entityReference is not null) { var entityType = entityReference.EntityType; if (convertedType != null) @@ -112,11 +113,30 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp { return ExpandSkipNavigation(root, entityReference, skipNavigation, convertedType is not null); } + } + + var structuralType = entityReference is not null + ? (ITypeBase)entityReference.EntityType + : innerExpression is ComplexPropertyReference complexReference + ? complexReference.Property.ComplexType + : null; + + if (structuralType is not null) + { + var complexProperty = memberIdentity.MemberInfo != null + ? structuralType.FindComplexProperty(memberIdentity.MemberInfo) + : memberIdentity.Name is not null + ? structuralType.FindComplexProperty(memberIdentity.Name) + : null; + if (complexProperty is not null) + { + return new ComplexPropertyReference(root, complexProperty); + } var property = memberIdentity.MemberInfo != null - ? entityType.FindProperty(memberIdentity.MemberInfo) + ? structuralType.FindProperty(memberIdentity.MemberInfo) : memberIdentity.Name is not null - ? entityType.FindProperty(memberIdentity.Name) + ? structuralType.FindProperty(memberIdentity.Name) : null; if (property?.IsPrimitiveCollection == true) { @@ -548,6 +568,7 @@ protected override Expression VisitExtension(Expression extensionExpression) case MaterializeCollectionNavigationExpression: case IncludeExpression: case PrimitiveCollectionReference: + case ComplexPropertyReference: return extensionExpression; } @@ -1002,6 +1023,9 @@ private sealed class ReducingExpressionVisitor : ExpressionVisitor case PrimitiveCollectionReference queryablePropertyReference: return Visit(queryablePropertyReference.Parent).CreateEFPropertyExpression(queryablePropertyReference.Property); + case ComplexPropertyReference complexPropertyReference: + return Visit(complexPropertyReference.Parent).CreateEFPropertyExpression(complexPropertyReference.Property, makeNullable: false); + case IncludeExpression includeExpression: var entityExpression = Visit(includeExpression.EntityExpression); var navigationExpression = ReplacingExpressionVisitor.Replace( diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs index 55429dc9937..fa2c54bec87 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs @@ -482,6 +482,40 @@ void IPrintableExpression.Print(ExpressionPrinter expressionPrinter) } } + /// + /// Queryable properties are not expanded (similar to . + /// + private sealed class ComplexPropertyReference(Expression parent, IComplexProperty property) : Expression, IPrintableExpression + { + protected override Expression VisitChildren(ExpressionVisitor visitor) + { + Parent = visitor.Visit(Parent); + + return this; + } + + public Expression Parent { get; private set; } = parent; + public new IComplexProperty Property { get; } = property; + + public override Type Type + => Property.ClrType; + + public override ExpressionType NodeType + => ExpressionType.Extension; + + void IPrintableExpression.Print(ExpressionPrinter expressionPrinter) + { + expressionPrinter.AppendLine(nameof(ComplexPropertyReference)); + using (expressionPrinter.Indent()) + { + expressionPrinter.Append("Parent: "); + expressionPrinter.Visit(Parent); + expressionPrinter.AppendLine(); + expressionPrinter.Append("Property: " + Property.Name); + } + } + } + /// /// Queryable properties are not expanded (similar to . /// diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs index 5f9baa1a5c7..45acce88fcd 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs @@ -2079,6 +2079,19 @@ private Expression UnwrapCollectionMaterialization(Expression expression) GetParameterName("o")); } + case ComplexPropertyReference complexCollectionReference: + { + var currentTree = new NavigationTreeExpression(Expression.Default(complexCollectionReference.Type.GetSequenceType())); + + return new NavigationExpansionExpression( + Expression.Call( + QueryableMethods.AsQueryable.MakeGenericMethod(complexCollectionReference.Type.GetSequenceType()), + complexCollectionReference), + currentTree, + currentTree, + GetParameterName("c")); + } + case PrimitiveCollectionReference primitiveCollectionReference: { var currentTree = new NavigationTreeExpression(Expression.Default(primitiveCollectionReference.Type.GetSequenceType())); diff --git a/src/EFCore/Query/Internal/EntityMaterializerSource.cs b/src/EFCore/Query/Internal/StructuralTypeMaterializerSource.cs similarity index 75% rename from src/EFCore/Query/Internal/EntityMaterializerSource.cs rename to src/EFCore/Query/Internal/StructuralTypeMaterializerSource.cs index 687f2fd0244..3fec2e421c5 100644 --- a/src/EFCore/Query/Internal/EntityMaterializerSource.cs +++ b/src/EFCore/Query/Internal/StructuralTypeMaterializerSource.cs @@ -1,10 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Concurrent; using Microsoft.EntityFrameworkCore.Diagnostics.Internal; using Microsoft.EntityFrameworkCore.Internal; -using Microsoft.EntityFrameworkCore.Metadata.Internal; +using static System.Linq.Expressions.Expression; namespace Microsoft.EntityFrameworkCore.Query.Internal; @@ -14,7 +13,7 @@ namespace Microsoft.EntityFrameworkCore.Query.Internal; /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// -public class EntityMaterializerSource : IEntityMaterializerSource +public class StructuralTypeMaterializerSource : IStructuralTypeMaterializerSource { private static readonly MethodInfo InjectableServiceInjectedMethod = typeof(IInjectableService).GetMethod(nameof(IInjectableService.Injected))!; @@ -29,7 +28,7 @@ private static readonly MethodInfo InjectableServiceInjectedMethod /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static readonly MethodInfo PopulateListMethod - = typeof(EntityMaterializerSource).GetMethod( + = typeof(StructuralTypeMaterializerSource).GetMethod( nameof(PopulateList), BindingFlags.Public | BindingFlags.Static)!; /// @@ -38,7 +37,7 @@ public static readonly MethodInfo PopulateListMethod /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public EntityMaterializerSource(EntityMaterializerSourceDependencies dependencies) + public StructuralTypeMaterializerSource(StructuralTypeMaterializerSourceDependencies dependencies) { Dependencies = dependencies; _bindingInterceptors = dependencies.SingletonInterceptors.OfType().ToList(); @@ -51,21 +50,7 @@ public EntityMaterializerSource(EntityMaterializerSourceDependencies dependencie /// /// Dependencies for this service. /// - protected virtual EntityMaterializerSourceDependencies Dependencies { get; } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - [Obsolete("Use the overload that accepts an EntityMaterializerSourceParameters object.")] - public virtual Expression CreateMaterializeExpression( - IEntityType entityType, - string entityInstanceName, - Expression materializationContextExpression) - => CreateMaterializeExpression( - new EntityMaterializerSourceParameters(entityType, entityInstanceName, null), materializationContextExpression); + protected virtual StructuralTypeMaterializerSourceDependencies Dependencies { get; } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -74,7 +59,7 @@ public virtual Expression CreateMaterializeExpression( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public Expression CreateMaterializeExpression( - EntityMaterializerSourceParameters parameters, + StructuralTypeMaterializerSourceParameters parameters, Expression materializationContextExpression) { var (structuralType, entityInstanceName) = (parameters.StructuralType, parameters.InstanceName); @@ -86,7 +71,7 @@ public Expression CreateMaterializeExpression( var constructorBinding = ModifyBindings(structuralType, structuralType.ConstructorBinding!); var bindingInfo = new ParameterBindingInfo(parameters, materializationContextExpression); - var instanceVariable = Expression.Variable(constructorBinding.RuntimeType, entityInstanceName); + var instanceVariable = Variable(constructorBinding.RuntimeType, entityInstanceName); bindingInfo.ServiceInstances.Add(instanceVariable); var properties = new HashSet( @@ -132,42 +117,40 @@ public Expression CreateMaterializeExpression( blockExpressions); } - private void AddInitializeExpressions( - HashSet properties, + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + protected virtual void AddInitializeExpression( + IPropertyBase property, ParameterBindingInfo bindingInfo, Expression instanceVariable, + MethodCallExpression valueBufferExpression, List blockExpressions) { - var valueBufferExpression = Expression.Call( - bindingInfo.MaterializationContextExpression, - MaterializationContext.GetValueBufferMethod); + var memberInfo = property.GetMemberInfo(forMaterialization: true, forSet: true); - foreach (var property in properties) + var valueExpression = property switch { - var memberInfo = property.GetMemberInfo(forMaterialization: true, forSet: true); + IProperty p => valueBufferExpression.CreateValueBufferReadValueExpression(memberInfo.GetMemberType(), p.GetIndex(), p), - var valueExpression = property switch - { - IProperty - => valueBufferExpression.CreateValueBufferReadValueExpression( - memberInfo.GetMemberType(), property.GetIndex(), property), + IServiceProperty serviceProperty + => serviceProperty.ParameterBinding.BindToParameter(bindingInfo), - IServiceProperty serviceProperty - => serviceProperty.ParameterBinding.BindToParameter(bindingInfo), + IComplexProperty { IsCollection: true } complexProperty + => Expression.Default(complexProperty.ClrType), // Initialize collections to null, they'll be populated separately - IComplexProperty complexProperty - => complexProperty.IsCollection - ? Expression.Default(complexProperty.ClrType) // Initialize collections to null, they'll be populated separately - : CreateMaterializeExpression( - new EntityMaterializerSourceParameters( - complexProperty.ComplexType, "complexType", QueryTrackingBehavior: null), - bindingInfo.MaterializationContextExpression), + IComplexProperty complexProperty + => CreateMaterializeExpression( + new StructuralTypeMaterializerSourceParameters(complexProperty.ComplexType, "complexType", QueryTrackingBehavior: null), + bindingInfo.MaterializationContextExpression), - _ => throw new UnreachableException() - }; + _ => throw new UnreachableException() + }; - blockExpressions.Add(CreateMemberAssignment(instanceVariable, memberInfo, property, valueExpression)); - } + blockExpressions.Add(CreateMemberAssignment(instanceVariable, memberInfo, property, valueExpression)); static Expression CreateMemberAssignment(Expression parameter, MemberInfo memberInfo, IPropertyBase property, Expression value) { @@ -178,25 +161,25 @@ static Expression CreateMemberAssignment(Expression parameter, MemberInfo member if (iCollectionInterface.IsAssignableFrom(property.ClrType)) { var genericMethod = PopulateListMethod.MakeGenericMethod(elementType); - var currentVariable = Expression.Variable(property.ClrType); + var currentVariable = Variable(property.ClrType); var convertedVariable = genericMethod.GetParameters()[1].ParameterType.IsAssignableFrom(currentVariable.Type) ? (Expression)currentVariable - : Expression.Convert(currentVariable, genericMethod.GetParameters()[1].ParameterType); - return Expression.Block( + : Convert(currentVariable, genericMethod.GetParameters()[1].ParameterType); + return Block( [currentVariable], - Expression.Assign( + Assign( currentVariable, - Expression.MakeMemberAccess(parameter, property.GetMemberInfo(forMaterialization: true, forSet: false))), - Expression.IfThenElse( - Expression.OrElse( - Expression.OrElse( - Expression.ReferenceEqual(currentVariable, Expression.Constant(null)), - Expression.ReferenceEqual(value, Expression.Constant(null))), - Expression.MakeMemberAccess( + MakeMemberAccess(parameter, property.GetMemberInfo(forMaterialization: true, forSet: false))), + IfThenElse( + OrElse( + OrElse( + ReferenceEqual(currentVariable, Constant(null)), + ReferenceEqual(value, Constant(null))), + MakeMemberAccess( currentVariable, iCollectionInterface.GetProperty(nameof(ICollection.IsReadOnly))!)), - Expression.MakeMemberAccess(parameter, memberInfo).Assign(value), - Expression.Call( + MakeMemberAccess(parameter, memberInfo).Assign(value), + Call( genericMethod, value, convertedVariable) @@ -205,11 +188,26 @@ static Expression CreateMemberAssignment(Expression parameter, MemberInfo member } return property.IsIndexerProperty() - ? Expression.Assign( - Expression.MakeIndex( - parameter, (PropertyInfo)memberInfo, new List { Expression.Constant(property.Name) }), + ? Assign( + MakeIndex(parameter, (PropertyInfo)memberInfo, [Constant(property.Name)]), value) - : Expression.MakeMemberAccess(parameter, memberInfo).Assign(value); + : MakeMemberAccess(parameter, memberInfo).Assign(value); + } + } + + private void AddInitializeExpressions( + HashSet properties, + ParameterBindingInfo bindingInfo, + Expression instanceVariable, + List blockExpressions) + { + var valueBufferExpression = Call( + bindingInfo.MaterializationContextExpression, + MaterializationContext.GetValueBufferMethod); + + foreach (var property in properties) + { + AddInitializeExpression(property, bindingInfo, instanceVariable, valueBufferExpression, blockExpressions); } } @@ -235,20 +233,20 @@ private static void AddAttachServiceExpressions( Expression instanceVariable, List blockExpressions) { - var getContext = Expression.Property(bindingInfo.MaterializationContextExpression, MaterializationContext.ContextProperty); + var getContext = Property(bindingInfo.MaterializationContextExpression, MaterializationContext.ContextProperty); foreach (var serviceInstance in bindingInfo.ServiceInstances) { blockExpressions.Add( - Expression.IfThen( - Expression.TypeIs(serviceInstance, typeof(IInjectableService)), - Expression.Call( - Expression.Convert(serviceInstance, typeof(IInjectableService)), + IfThen( + TypeIs(serviceInstance, typeof(IInjectableService)), + Call( + Convert(serviceInstance, typeof(IInjectableService)), InjectableServiceInjectedMethod, getContext, instanceVariable, - Expression.Constant(bindingInfo.QueryTrackingBehavior, typeof(QueryTrackingBehavior?)), - Expression.Constant(bindingInfo.StructuralType)))); + Constant(bindingInfo.QueryTrackingBehavior, typeof(QueryTrackingBehavior?)), + Constant(bindingInfo.StructuralType)))); } } @@ -298,7 +296,7 @@ private Expression CreateMaterializeExpression( HashSet properties, ParameterBindingInfo bindingInfo) { - blockExpressions.Add(Expression.Assign(instanceVariable, constructorExpression)); + blockExpressions.Add(Assign(instanceVariable, constructorExpression)); AddInitializeExpressions(properties, bindingInfo, instanceVariable, blockExpressions); @@ -309,7 +307,7 @@ private Expression CreateMaterializeExpression( blockExpressions.Add(instanceVariable); - return Expression.Block(bindingInfo.ServiceInstances, blockExpressions); + return Block(bindingInfo.ServiceInstances, blockExpressions); } private Expression CreateInterceptionMaterializeExpression( @@ -337,47 +335,47 @@ private Expression CreateInterceptionMaterializeExpression( // // return instance; - var materializationDataVariable = Expression.Variable(typeof(MaterializationInterceptionData), "materializationData"); - var creatingResultVariable = Expression.Variable(typeof(InterceptionResult), "creatingResult"); - var interceptorExpression = Expression.Constant(materializationInterceptor, typeof(IMaterializationInterceptor)); - var accessorDictionaryVariable = Expression.Variable( + var materializationDataVariable = Variable(typeof(MaterializationInterceptionData), "materializationData"); + var creatingResultVariable = Variable(typeof(InterceptionResult), "creatingResult"); + var interceptorExpression = Constant(materializationInterceptor, typeof(IMaterializationInterceptor)); + var accessorDictionaryVariable = Variable( typeof(Dictionary)>), "accessorDictionary"); blockExpressions.Add( - Expression.Assign( + Assign( accessorDictionaryVariable, CreateAccessorDictionaryExpression())); blockExpressions.Add( - Expression.Assign( + Assign( materializationDataVariable, - Expression.New( + New( MaterializationInterceptionDataConstructor, bindingInfo.MaterializationContextExpression, - Expression.Constant(structuralType), - Expression.Constant(bindingInfo.QueryTrackingBehavior, typeof(QueryTrackingBehavior?)), + Constant(structuralType), + Constant(bindingInfo.QueryTrackingBehavior, typeof(QueryTrackingBehavior?)), accessorDictionaryVariable))); blockExpressions.Add( - Expression.Assign( + Assign( creatingResultVariable, - Expression.Call( + Call( interceptorExpression, CreatingInstanceMethod, materializationDataVariable, - Expression.Default(typeof(InterceptionResult))))); + Default(typeof(InterceptionResult))))); blockExpressions.Add( - Expression.Assign( + Assign( instanceVariable, - Expression.Convert( - Expression.Call( + Convert( + Call( interceptorExpression, CreatedInstanceMethod, materializationDataVariable, - Expression.Condition( - Expression.Property( + Condition( + Property( creatingResultVariable, HasResultMethod), - Expression.Convert( - Expression.Property( + Convert( + Property( creatingResultVariable, ResultProperty), instanceVariable.Type), @@ -385,28 +383,28 @@ private Expression CreateInterceptionMaterializeExpression( instanceVariable.Type))); blockExpressions.Add( properties.Count == 0 - ? Expression.Call( + ? Call( interceptorExpression, InitializingInstanceMethod, materializationDataVariable, instanceVariable, - Expression.Default(typeof(InterceptionResult))) - : Expression.IfThen( - Expression.Not( - Expression.Property( - Expression.Call( + Default(typeof(InterceptionResult))) + : IfThen( + Not( + Property( + Call( interceptorExpression, InitializingInstanceMethod, materializationDataVariable, instanceVariable, - Expression.Default(typeof(InterceptionResult))), + Default(typeof(InterceptionResult))), IsSuppressedProperty)), CreateInitializeExpression())); blockExpressions.Add( - Expression.Assign( + Assign( instanceVariable, - Expression.Convert( - Expression.Call( + Convert( + Call( interceptorExpression, InitializedInstanceMethod, materializationDataVariable, @@ -414,21 +412,21 @@ private Expression CreateInterceptionMaterializeExpression( instanceVariable.Type))); blockExpressions.Add(instanceVariable); - return Expression.Block( + return Block( bindingInfo.ServiceInstances.Concat([accessorDictionaryVariable, materializationDataVariable, creatingResultVariable]), blockExpressions); BlockExpression CreateAccessorDictionaryExpression() { - var dictionaryVariable = Expression.Variable( + var dictionaryVariable = Variable( typeof(Dictionary)>), "dictionary"); - var valueBufferExpression = Expression.Call( + var valueBufferExpression = Call( bindingInfo.MaterializationContextExpression, MaterializationContext.GetValueBufferMethod); var snapshotBlockExpressions = new List { - Expression.Assign( + Assign( dictionaryVariable, - Expression.New( + New( typeof(Dictionary)>) .GetConstructor(Type.EmptyTypes)!)) }; @@ -438,25 +436,25 @@ BlockExpression CreateAccessorDictionaryExpression() foreach (var property in entityType.GetServiceProperties().Cast().Concat(structuralType.GetProperties())) { snapshotBlockExpressions.Add( - Expression.Call( + Call( dictionaryVariable, DictionaryAddMethod, - Expression.Constant(property), - Expression.New( + Constant(property), + New( DictionaryConstructor, - Expression.Lambda( + Lambda( typeof(Func<,>).MakeGenericType(typeof(MaterializationContext), property.ClrType), CreateAccessorReadExpression(), (ParameterExpression)bindingInfo.MaterializationContextExpression), - Expression.Lambda>( - Expression.Convert(CreateAccessorReadExpression(), typeof(object)), + Lambda>( + Convert(CreateAccessorReadExpression(), typeof(object)), (ParameterExpression)bindingInfo.MaterializationContextExpression)))); Expression CreateAccessorReadExpression() => property is IServiceProperty serviceProperty ? serviceProperty.ParameterBinding.BindToParameter(bindingInfo) : (property as IProperty)?.IsPrimaryKey() == true - ? Expression.Convert( + ? Convert( valueBufferExpression.CreateValueBufferReadValueExpression( typeof(object), property.GetIndex(), @@ -471,7 +469,7 @@ Expression CreateAccessorReadExpression() snapshotBlockExpressions.Add(dictionaryVariable); - return Expression.Block([dictionaryVariable], snapshotBlockExpressions); + return Block([dictionaryVariable], snapshotBlockExpressions); } BlockExpression CreateInitializeExpression() @@ -485,7 +483,7 @@ BlockExpression CreateInitializeExpression() AddAttachServiceExpressions(bindingInfo, instanceVariable, blockExpressions); } - return Expression.Block(initializeBlockExpressions); + return Block(initializeBlockExpressions); } } @@ -499,11 +497,11 @@ public virtual Func GetMaterializer( IEntityType entityType) { var materializationContextParameter - = Expression.Parameter(typeof(MaterializationContext), "materializationContext"); + = Parameter(typeof(MaterializationContext), "materializationContext"); - return Expression.Lambda>( - ((IEntityMaterializerSource)this).CreateMaterializeExpression( - new EntityMaterializerSourceParameters(entityType, "instance", null), materializationContextParameter), + return Lambda>( + ((IStructuralTypeMaterializerSource)this).CreateMaterializeExpression( + new StructuralTypeMaterializerSourceParameters(entityType, "instance", null), materializationContextParameter), materializationContextParameter) .Compile(); } @@ -516,12 +514,11 @@ var materializationContextParameter /// public virtual Func GetMaterializer(IComplexType complexType) { - var materializationContextParameter - = Expression.Parameter(typeof(MaterializationContext), "materializationContext"); + var materializationContextParameter = Parameter(typeof(MaterializationContext), "materializationContext"); - return Expression.Lambda>( - ((IEntityMaterializerSource)this).CreateMaterializeExpression( - new EntityMaterializerSourceParameters(complexType, "instance", null), materializationContextParameter), + return Lambda>( + ((IStructuralTypeMaterializerSource)this).CreateMaterializeExpression( + new StructuralTypeMaterializerSourceParameters(complexType, "instance", null), materializationContextParameter), materializationContextParameter) .Compile(); } @@ -573,12 +570,12 @@ public virtual Func GetEmptyMaterializer( { binding = ModifyBindings(entityType, binding); - var materializationContextExpression = Expression.Parameter(typeof(MaterializationContext), "mc"); + var materializationContextExpression = Parameter(typeof(MaterializationContext), "mc"); var bindingInfo = new ParameterBindingInfo( - new EntityMaterializerSourceParameters(entityType, "instance", null), materializationContextExpression); + new StructuralTypeMaterializerSourceParameters(entityType, "instance", null), materializationContextExpression); var blockExpressions = new List(); - var instanceVariable = Expression.Variable(binding.RuntimeType, "instance"); + var instanceVariable = Variable(binding.RuntimeType, "instance"); bindingInfo.ServiceInstances.Add(instanceVariable); CreateServiceInstances(binding, bindingInfo, blockExpressions, serviceProperties); @@ -591,7 +588,7 @@ public virtual Func GetEmptyMaterializer( properties.Remove(consumedProperty); } - return Expression.Lambda>( + return Lambda>( _materializationInterceptor == null ? properties.Count == 0 && blockExpressions.Count == 0 ? constructorExpression @@ -630,8 +627,8 @@ private static void CreateServiceInstances( { if (bindingInfo.ServiceInstances.All(s => s.Type != parameterBinding.ServiceType)) { - var variable = Expression.Variable(parameterBinding.ServiceType); - blockExpressions.Add(Expression.Assign(variable, parameterBinding.BindToParameter(bindingInfo))); + var variable = Variable(parameterBinding.ServiceType); + blockExpressions.Add(Assign(variable, parameterBinding.BindToParameter(bindingInfo))); bindingInfo.ServiceInstances.Add(variable); } } @@ -641,8 +638,8 @@ private static void CreateServiceInstances( var serviceType = serviceProperty.ParameterBinding.ServiceType; if (bindingInfo.ServiceInstances.All(e => e.Type != serviceType)) { - var variable = Expression.Variable(serviceType); - blockExpressions.Add(Expression.Assign(variable, serviceProperty.ParameterBinding.BindToParameter(bindingInfo))); + var variable = Variable(serviceType); + blockExpressions.Add(Assign(variable, serviceProperty.ParameterBinding.BindToParameter(bindingInfo))); bindingInfo.ServiceInstances.Add(variable); } } diff --git a/src/EFCore/Query/Internal/EntityMaterializerSourceDependencies.cs b/src/EFCore/Query/Internal/StructuralTypeMaterializerSourceDependencies.cs similarity index 93% rename from src/EFCore/Query/Internal/EntityMaterializerSourceDependencies.cs rename to src/EFCore/Query/Internal/StructuralTypeMaterializerSourceDependencies.cs index d59b5840c5a..19841b2292e 100644 --- a/src/EFCore/Query/Internal/EntityMaterializerSourceDependencies.cs +++ b/src/EFCore/Query/Internal/StructuralTypeMaterializerSourceDependencies.cs @@ -5,7 +5,7 @@ namespace Microsoft.EntityFrameworkCore.Query.Internal; /// /// -/// Service dependencies parameter class for +/// Service dependencies parameter class for /// /// /// This type is typically used by database providers (and other extensions). It is generally @@ -27,7 +27,7 @@ namespace Microsoft.EntityFrameworkCore.Query.Internal; /// This service cannot depend on services registered as . /// /// -public sealed record EntityMaterializerSourceDependencies +public sealed record StructuralTypeMaterializerSourceDependencies { /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -44,7 +44,7 @@ public sealed record EntityMaterializerSourceDependencies /// the constructor at any point in this process. /// [EntityFrameworkInternal] - public EntityMaterializerSourceDependencies(IEnumerable singletonInterceptors) + public StructuralTypeMaterializerSourceDependencies(IEnumerable singletonInterceptors) => SingletonInterceptors = singletonInterceptors; /// diff --git a/src/EFCore/Query/LiftableConstantExpressionHelpers.cs b/src/EFCore/Query/LiftableConstantExpressionHelpers.cs index 36bb49df8d7..a372036b624 100644 --- a/src/EFCore/Query/LiftableConstantExpressionHelpers.cs +++ b/src/EFCore/Query/LiftableConstantExpressionHelpers.cs @@ -39,8 +39,8 @@ public static class LiftableConstantExpressionHelpers private static readonly MethodInfo EntityTypeFindSkipNavigationMethod = typeof(IEntityType).GetRuntimeMethod(nameof(IEntityType.FindSkipNavigation), [typeof(string)])!; - private static readonly MethodInfo NavigationBaseClrCollectionAccessorMethod = - typeof(INavigationBase).GetRuntimeMethod(nameof(INavigationBase.GetCollectionAccessor), [])!; + private static readonly MethodInfo PropertyBaseClrCollectionAccessorMethod = + typeof(IPropertyBase).GetRuntimeMethod(nameof(IPropertyBase.GetCollectionAccessor), [])!; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -201,20 +201,27 @@ public static Expression> Buil /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public static Expression BuildNavigationAccess(INavigationBase? navigation, ParameterExpression liftableConstantContextParameter) + public static Expression BuildRelationshipAccess(IPropertyBase? relationship, ParameterExpression liftableConstantContextParameter) { - if (navigation == null) + if (relationship is null) { return Default(typeof(INavigationBase)); } - var declaringType = navigation.DeclaringType; + var declaringType = relationship.DeclaringType; var declaringTypeExpression = BuildMemberAccessForEntityOrComplexType(declaringType, liftableConstantContextParameter); var result = Call( declaringTypeExpression, - navigation is ISkipNavigation ? EntityTypeFindSkipNavigationMethod : EntityTypeFindNavigationMethod, - Constant(navigation.Name)); + relationship switch + { + ISkipNavigation => EntityTypeFindSkipNavigationMethod, + INavigation => EntityTypeFindNavigationMethod, + IComplexProperty => TypeBaseFindComplexPropertyMethod, + + _ => throw new UnreachableException() + }, + Constant(relationship.Name)); return result; } @@ -225,10 +232,10 @@ public static Expression BuildNavigationAccess(INavigationBase? navigation, Para /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public static Expression> BuildNavigationAccessLambda(INavigationBase? navigation) + public static Expression> BuildRelationshipAccessLambda(IPropertyBase? relationship) { var prm = Parameter(typeof(MaterializerLiftableConstantContext)); - var body = BuildNavigationAccess(navigation, prm); + var body = BuildRelationshipAccess(relationship, prm); return Lambda>(body, prm); } @@ -239,15 +246,15 @@ public static Expression> Buil /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public static Expression BuildClrCollectionAccessor(INavigationBase? navigation, ParameterExpression liftableConstantContextParameter) + public static Expression BuildClrCollectionAccessor(IPropertyBase? relationship, ParameterExpression liftableConstantContextParameter) { - if (navigation == null) + if (relationship is null) { return Default(typeof(IClrCollectionAccessor)); } - var navigationAccessExpression = BuildNavigationAccess(navigation, liftableConstantContextParameter); - var result = Call(navigationAccessExpression, NavigationBaseClrCollectionAccessorMethod); + var relationshipAccessExpression = BuildRelationshipAccess(relationship, liftableConstantContextParameter); + var result = Call(relationshipAccessExpression, PropertyBaseClrCollectionAccessorMethod); return result; } @@ -259,10 +266,10 @@ public static Expression BuildClrCollectionAccessor(INavigationBase? navigation, /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static Expression> BuildClrCollectionAccessorLambda( - INavigationBase? navigation) + IPropertyBase? relationship) { var prm = Parameter(typeof(MaterializerLiftableConstantContext)); - var body = BuildClrCollectionAccessor(navigation, prm); + var body = BuildClrCollectionAccessor(relationship, prm); return Lambda>(body, prm); } diff --git a/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs b/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs index 481ed64f9a2..bec097af0b9 100644 --- a/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs +++ b/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs @@ -37,7 +37,7 @@ private static readonly PropertyInfo CancellationTokenMemberInfo = typeof(QueryContext).GetTypeInfo().GetProperty(nameof(QueryContext.CancellationToken))!; private readonly Expression _cancellationTokenParameter; - private readonly EntityMaterializerInjectingExpressionVisitor _entityMaterializerInjectingExpressionVisitor; + private readonly StructuralTypeMaterializerInjector _structuralTypeMaterializerInjector; private readonly ConstantVerifyingExpressionVisitor _constantVerifyingExpressionVisitor; private readonly MaterializationConditionConstantLifter _materializationConditionConstantLifter; @@ -53,8 +53,8 @@ protected ShapedQueryCompilingExpressionVisitor( Dependencies = dependencies; QueryCompilationContext = queryCompilationContext; - _entityMaterializerInjectingExpressionVisitor = - new EntityMaterializerInjectingExpressionVisitor( + _structuralTypeMaterializerInjector = + new StructuralTypeMaterializerInjector( dependencies.EntityMaterializerSource, dependencies.LiftableConstantFactory, queryCompilationContext.QueryTrackingBehavior, @@ -197,17 +197,24 @@ public static async Task SingleAsync( /// An expression of enumerable. protected abstract Expression VisitShapedQuery(ShapedQueryExpression shapedQueryExpression); + /// + /// This method has been obsoleted, see . + /// + [Obsolete("Use InjectStructuralTypeMaterializers instead.", error: true)] + protected virtual Expression InjectEntityMaterializers(Expression expression) + => throw new UnreachableException(); + /// /// Inject entity materializers in given shaper expression. is replaced with materializer /// expression for given entity. /// /// The expression to inject entity materializers. /// A expression with entity materializers injected. - protected virtual Expression InjectEntityMaterializers(Expression expression) + protected virtual Expression InjectStructuralTypeMaterializers(Expression expression) { VerifyNoClientConstant(expression); - var materializerExpression = _entityMaterializerInjectingExpressionVisitor.Inject(expression); + var materializerExpression = _structuralTypeMaterializerInjector.Inject(expression); if (QueryCompilationContext.SupportsPrecompiledQuery) { materializerExpression = _materializationConditionConstantLifter.Visit(materializerExpression); @@ -354,8 +361,8 @@ protected override Expression VisitExtension(Expression extensionExpression) } } - private sealed class EntityMaterializerInjectingExpressionVisitor( - IEntityMaterializerSource entityMaterializerSource, + private sealed class StructuralTypeMaterializerInjector( + IStructuralTypeMaterializerSource entityMaterializerSource, ILiftableConstantFactory liftableConstantFactory, QueryTrackingBehavior queryTrackingBehavior, bool supportsPrecompiledQuery) @@ -704,7 +711,7 @@ private BlockExpression CreateFullMaterializeExpression( var materializer = entityMaterializerSource .CreateMaterializeExpression( - new EntityMaterializerSourceParameters( + new StructuralTypeMaterializerSourceParameters( concreteTypeBase, "instance", queryTrackingBehavior), materializationContextVariable); // TODO: Properly support shadow properties for complex types #35613 diff --git a/src/EFCore/Query/ShapedQueryCompilingExpressionVisitorDependencies.cs b/src/EFCore/Query/ShapedQueryCompilingExpressionVisitorDependencies.cs index 5c1b72f8675..c8303f2060f 100644 --- a/src/EFCore/Query/ShapedQueryCompilingExpressionVisitorDependencies.cs +++ b/src/EFCore/Query/ShapedQueryCompilingExpressionVisitorDependencies.cs @@ -49,7 +49,7 @@ public sealed record ShapedQueryCompilingExpressionVisitorDependencies /// [EntityFrameworkInternal] public ShapedQueryCompilingExpressionVisitorDependencies( - IEntityMaterializerSource entityMaterializerSource, + IStructuralTypeMaterializerSource entityMaterializerSource, ITypeMappingSource typeMappingSource, IMemoryCache memoryCache, ICoreSingletonOptions coreSingletonOptions, @@ -73,7 +73,7 @@ public ShapedQueryCompilingExpressionVisitorDependencies( /// /// The materializer source. /// - public IEntityMaterializerSource EntityMaterializerSource { get; init; } + public IStructuralTypeMaterializerSource EntityMaterializerSource { get; init; } /// /// The type mapping source. diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsCollectionCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsCollectionCosmosTest.cs new file mode 100644 index 00000000000..9b8ae7ae5d4 --- /dev/null +++ b/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsCollectionCosmosTest.cs @@ -0,0 +1,134 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Azure.Cosmos; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; + +public class OwnedNavigationsCollectionCosmosTest : OwnedNavigationsCollectionTestBase +{ + public OwnedNavigationsCollectionCosmosTest(OwnedNavigationsCosmosFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override Task Count(bool async) + => Fixture.NoSyncTest( + async, async a => + { + await base.Count(a); + + AssertSql( + """ +SELECT VALUE c +FROM root c +WHERE (ARRAY_LENGTH(c["RelatedCollection"]) = 2) +"""); + }); + + public override Task Where(bool async) + => Fixture.NoSyncTest( + async, async a => + { + await base.Where(a); + + AssertSql( + """ +SELECT VALUE c +FROM root c +WHERE (( + SELECT VALUE COUNT(1) + FROM r IN c["RelatedCollection"] + WHERE (r["Int"] != 50)) = 2) +"""); + }); + + public override async Task OrderBy_ElementAt(bool async) + { + if (async) + { + // 'ORDER BY' is not supported in subqueries. + await Assert.ThrowsAsync(() => base.OrderBy_ElementAt(async)); + + AssertSql( + """ +SELECT VALUE c +FROM root c +WHERE (ARRAY( + SELECT VALUE r["Int"] + FROM r IN c["RelatedCollection"] + ORDER BY r["Id"])[0] = 21) +"""); + } + } + + public override Task Index_constant(bool async) + => Fixture.NoSyncTest( + async, async a => + { + await base.Index_constant(a); + + AssertSql( + """ +SELECT VALUE c +FROM root c +WHERE (c["RelatedCollection"][0]["Int"] = 21) +"""); + }); + + public override Task Index_parameter(bool async) + => Fixture.NoSyncTest( + async, async a => + { + await base.Index_parameter(a); + + AssertSql( + """ +@i=? + +SELECT VALUE c +FROM root c +WHERE (c["RelatedCollection"][@i]["Int"] = 21) +"""); + }); + + public override async Task Index_column(bool async) + { + if (async) + { + // The specified query includes 'member indexer' which is currently not supported + await Assert.ThrowsAsync(() => base.Index_column(async)); + + AssertSql( + """ +SELECT VALUE c +FROM root c +WHERE (c["RelatedCollection"][(c["Id"] - 1)]["Int"] = 21) +"""); + } + } + + public override async Task Index_out_of_bounds(bool async) + { + if (async) + { + await base.Index_out_of_bounds(async); + + AssertSql( + """ +SELECT VALUE c +FROM root c +WHERE (c["RelatedCollection"][9999]["Int"] = 50) +"""); + } + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsCosmosFixture.cs b/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsCosmosFixture.cs index 27b9d883344..e03c4f7c043 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsCosmosFixture.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsCosmosFixture.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; - namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; public class OwnedNavigationsCosmosFixture : OwnedNavigationsFixtureBase @@ -28,12 +26,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con { base.OnModelCreating(modelBuilder, context); - modelBuilder.Entity() - .ToContainer("RootEntities") - .HasDiscriminatorInJsonId() - .HasDiscriminator("Discriminator").HasValue("Root"); + modelBuilder.Ignore(); - modelBuilder.Entity() - .ToContainer("RootEntities"); + modelBuilder.Entity() + .ToContainer("RootEntities") + .HasNoDiscriminator(); } } diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsMiscellaneousCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsMiscellaneousCosmosTest.cs new file mode 100644 index 00000000000..8e4e9393b92 --- /dev/null +++ b/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsMiscellaneousCosmosTest.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; + +public class OwnedNavigationsMiscellaneousCosmosTest : OwnedNavigationsMiscellaneousTestBase +{ + public OwnedNavigationsMiscellaneousCosmosTest(OwnedNavigationsCosmosFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public override Task Where_related_property(bool async) + => Fixture.NoSyncTest( + async, async a => + { + await base.Where_related_property(a); + + AssertSql( + """ +SELECT VALUE c +FROM root c +WHERE (c["RequiredRelated"]["Int"] = 8) +"""); + }); + + public override Task Where_optional_related_property(bool async) + => Fixture.NoSyncTest( + async, async a => + { + await base.Where_optional_related_property(a); + + AssertSql( + """ +SELECT VALUE c +FROM root c +WHERE (c["OptionalRelated"]["Int"] = 9) +"""); + }); + + public override Task Where_nested_related_property(bool async) + => Fixture.NoSyncTest( + async, async a => + { + await base.Where_nested_related_property(a); + + AssertSql( + """ +SELECT VALUE c +FROM root c +WHERE (c["RequiredRelated"]["RequiredNested"]["Int"] = 50) +"""); + }); + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionCosmosTest.cs index 7205aa95ac7..03391f8e8f4 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionCosmosTest.cs @@ -14,257 +14,178 @@ public OwnedNavigationsProjectionCosmosTest(OwnedNavigationsCosmosFixture fixtur Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } - public override async Task Select_root(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - if (async) - { - await base.Select_root(async, queryTrackingBehavior); + public override Task Select_root(bool async, QueryTrackingBehavior queryTrackingBehavior) + => Fixture.NoSyncTest( + async, async a => + { + await base.Select_root(a, queryTrackingBehavior); - AssertSql( - """ + AssertSql( + """ SELECT VALUE c FROM root c """); - } - } + }); - public override async Task Select_trunk_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - if (async) - { - await base.Select_trunk_optional(async, queryTrackingBehavior); + #region Simple properties - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + public override Task Select_related_property(bool async, QueryTrackingBehavior queryTrackingBehavior) + => Fixture.NoSyncTest( + async, async a => { + await base.Select_related_property(a, queryTrackingBehavior); + AssertSql( """ -SELECT VALUE c +SELECT VALUE c["RequiredRelated"]["String"] FROM root c -ORDER BY c["Id"] """); - } - } - } - - public override async Task Select_trunk_required(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - if (async) - { - await base.Select_trunk_required(async, queryTrackingBehavior); + }); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + public override Task Select_optional_related_property(bool async, QueryTrackingBehavior queryTrackingBehavior) + => Fixture.NoSyncTest( + async, async a => { + await base.Select_optional_related_property(a, queryTrackingBehavior); + AssertSql( """ -SELECT VALUE c +SELECT VALUE c["OptionalRelated"]["String"] FROM root c -ORDER BY c["Id"] """); - } - } - } + }); - public override async Task Select_trunk_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - if (async) - { - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + public override Task Select_optional_related_property_value_type(bool async, QueryTrackingBehavior queryTrackingBehavior) + => Fixture.NoSyncTest( + async, async a => { - await base.Select_trunk_collection(async, queryTrackingBehavior); + await base.Select_optional_related_property_value_type(a, queryTrackingBehavior); AssertSql( """ -SELECT VALUE c +SELECT VALUE c["OptionalRelated"]["Int"] FROM root c -ORDER BY c["Id"] """); - } - } - } + }); - public override async Task Select_branch_required_required(bool async, QueryTrackingBehavior queryTrackingBehavior) + #endregion Simple properties + + #region Non-collection + + public override async Task Select_related(bool async, QueryTrackingBehavior queryTrackingBehavior) { if (async) { - await base.Select_branch_required_required(async, queryTrackingBehavior); + await base.Select_related(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ SELECT VALUE c FROM root c -ORDER BY c["Id"] """); } } } - public override async Task Select_branch_required_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related(bool async, QueryTrackingBehavior queryTrackingBehavior) { if (async) { - await base.Select_branch_required_optional(async, queryTrackingBehavior); + await base.Select_optional_related(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ SELECT VALUE c FROM root c -ORDER BY c["Id"] """); } } } - public override async Task Select_branch_optional_required(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_required_related_required_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { if (async) { - await base.Select_branch_optional_required(async, queryTrackingBehavior); + await base.Select_required_related_required_nested(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ SELECT VALUE c FROM root c -ORDER BY c["Id"] """); } } } - public override async Task Select_branch_optional_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_required_related_optional_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { if (async) { - await base.Select_branch_optional_optional(async, queryTrackingBehavior); + await base.Select_required_related_optional_nested(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ SELECT VALUE c FROM root c -ORDER BY c["Id"] """); } } } - public override async Task Select_branch_required_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_required_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { if (async) { - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - await base.Select_branch_required_collection(async, queryTrackingBehavior); + await base.Select_optional_related_required_nested(async, queryTrackingBehavior); - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { - //issue #31696 - await Assert.ThrowsAsync( - () => base.Select_branch_required_collection(async, queryTrackingBehavior)); - AssertSql( """ SELECT VALUE c FROM root c -ORDER BY c["Id"] """); } } } - public override async Task Select_branch_optional_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_optional_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { if (async) { - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - await base.Select_branch_required_collection(async, queryTrackingBehavior); + await base.Select_optional_related_optional_nested(async, queryTrackingBehavior); - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { - //issue #31696 - await Assert.ThrowsAsync( - () => base.Select_branch_optional_collection(async, queryTrackingBehavior)); - AssertSql( """ SELECT VALUE c FROM root c -ORDER BY c["Id"] """); } } } - #region Multiple + #endregion Non-collection - public override Task Select_root_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) - => Fixture.NoSyncTest( - async, async a => - { - await base.Select_root_duplicated(a, queryTrackingBehavior); + #region Collection - AssertSql( - """ -SELECT VALUE c -FROM root c -"""); - }); - - public override async Task Select_trunk_and_branch_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { if (async) { - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - await base.Select_branch_required_collection(async, queryTrackingBehavior); + await base.Select_related_collection(async, queryTrackingBehavior); - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { - //issue #31696 - await Assert.ThrowsAsync( - () => base.Select_trunk_and_branch_duplicated(async, queryTrackingBehavior)); - AssertSql( """ SELECT VALUE c @@ -275,45 +196,25 @@ ORDER BY c["Id"] } } - public override async Task Select_trunk_and_trunk_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - if (async) + if (async && queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - await base.Select_branch_required_collection(async, queryTrackingBehavior); - - AssertSql(); - } - else - { - //issue #31696 - await Assert.ThrowsAsync( - () => base.Select_trunk_and_trunk_duplicated(async, queryTrackingBehavior)); + await Assert.ThrowsAsync(() => base.Select_required_related_nested_collection(async, queryTrackingBehavior)); - AssertSql( - """ + AssertSql( + """ SELECT VALUE c FROM root c -ORDER BY c["Id"] """); - } } } - public override async Task Select_leaf_trunk_root(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - if (async) + if (async && queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - // This particular scenario does not throw an exception in TrackAll mode as it's supposed to - await Assert.ThrowsAsync(() => base.Select_leaf_trunk_root(async, queryTrackingBehavior)); - } - else - { - await base.Select_leaf_trunk_root(async, queryTrackingBehavior); - } + await Assert.ThrowsAsync(() => base.Select_optional_related_nested_collection(async, queryTrackingBehavior)); AssertSql( """ @@ -323,151 +224,77 @@ FROM root c } } - public override async Task Select_multiple_branch_leaf(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - if (async) - { - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - await base.Select_branch_required_collection(async, queryTrackingBehavior); - } - else - { - //issue #35702 - await Assert.ThrowsAsync( - () => base.Select_multiple_branch_leaf(async, queryTrackingBehavior)); - } - - AssertSql(); - } - } - - #endregion Multiple - - #region Subquery - - public override async Task Select_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task SelectMany_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - // For TrackAll, the base implementation expects a different exception to be thrown - if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + if (async && queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { - await AssertTranslationFailed( - () => base.Select_subquery_root_set_required_trunk_FirstOrDefault_branch(async, queryTrackingBehavior)); + // The given key 'n' was not present in the dictionary + await Assert.ThrowsAsync(() => base.SelectMany_related_collection(async, queryTrackingBehavior)); AssertSql(); } } - public override async Task Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task SelectMany_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - // For TrackAll, the base implementation expects a different exception to be thrown - if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + if (async && queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { - await AssertTranslationFailed( - () => base.Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(async, queryTrackingBehavior)); + // The given key 'n' was not present in the dictionary + await Assert.ThrowsAsync(() => base.SelectMany_required_related_nested_collection(async, queryTrackingBehavior)); AssertSql(); } } - public override async Task Select_subquery_root_set_trunk_FirstOrDefault_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task SelectMany_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - // For TrackAll, the base implementation expects a different exception to be thrown - if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + if (async && queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { - await AssertTranslationFailed( - () => base.Select_subquery_root_set_trunk_FirstOrDefault_collection(async, queryTrackingBehavior)); + // The given key 'n' was not present in the dictionary + await Assert.ThrowsAsync(() => base.SelectMany_optional_related_nested_collection(async, queryTrackingBehavior)); AssertSql(); } } - public override async Task Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - // For TrackAll, the base implementation expects a different exception to be thrown - if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) - { - await AssertTranslationFailed( - () => base.Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(async, queryTrackingBehavior)); - - AssertSql(); - } - } - - public override async Task Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - // For TrackAll, the base implementation expects a different exception to be thrown - if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) - { - await AssertTranslationFailed( - () => base.Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(async, queryTrackingBehavior)); + #endregion Collection - AssertSql(); - } - } + #region Multiple - #endregion Subquery + public override Task Select_root_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + => Fixture.NoSyncTest( + async, async a => + { + await base.Select_root_duplicated(a, queryTrackingBehavior); - #region SelectMany + AssertSql( + """ +SELECT VALUE c +FROM root c +"""); + }); - public override async Task SelectMany_trunk_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - if (async) - { - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - await base.Select_branch_required_collection(async, queryTrackingBehavior); - } - else - { - //issue #34349 - await Assert.ThrowsAsync( - () => base.SelectMany_trunk_collection(async, queryTrackingBehavior)); - } + #endregion Multiple - AssertSql(); - } - } + #region Subquery - public override async Task SelectMany_required_trunk_reference_branch_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_subquery_required_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) { - if (async) + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - await base.Select_branch_required_collection(async, queryTrackingBehavior); - } - else - { - //issue #34349 - await Assert.ThrowsAsync( - () => base.SelectMany_required_trunk_reference_branch_collection(async, queryTrackingBehavior)); - } - - AssertSql(); + await AssertTranslationFailed(() => base.Select_subquery_required_related_FirstOrDefault(async, queryTrackingBehavior)); } } - public override async Task SelectMany_optional_trunk_reference_branch_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_subquery_optional_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) { - if (async) + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - await base.Select_branch_required_collection(async, queryTrackingBehavior); - } - else - { - //issue #34349 - await Assert.ThrowsAsync( - () => base.SelectMany_optional_trunk_reference_branch_collection(async, queryTrackingBehavior)); - } - - AssertSql(); + await AssertTranslationFailed(() => base.Select_subquery_required_related_FirstOrDefault(async, queryTrackingBehavior)); } } - #endregion SelectMany + #endregion Subquery [ConditionalFact] public virtual void Check_all_tests_overridden() diff --git a/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs b/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs index 4a728dbe158..dc0b37d2fca 100644 --- a/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs +++ b/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs @@ -409,7 +409,7 @@ private async Task CreateContainersAsync(DbContext context) // TODO: see issue #35854 // once Azure.ResourceManager.CosmosDB package supports vectors and FTS, those need to be added here - + await database.Value.GetCosmosDBSqlContainers().CreateOrUpdateAsync( WaitUntil.Completed, container.Id, content).ConfigureAwait(false); } @@ -850,10 +850,10 @@ public IEnumerable GetReferencingForeignKeys() public IEnumerable GetServiceProperties() => throw new NotImplementedException(); - public Func GetOrCreateMaterializer(IEntityMaterializerSource source) + public Func GetOrCreateMaterializer(IStructuralTypeMaterializerSource source) => throw new NotImplementedException(); - public Func GetOrCreateEmptyMaterializer(IEntityMaterializerSource source) + public Func GetOrCreateEmptyMaterializer(IStructuralTypeMaterializerSource source) => throw new NotImplementedException(); public IEnumerable GetSkipNavigations() diff --git a/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs b/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs index b093b238af4..d117ab645d6 100644 --- a/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs @@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore.BulkUpdates; using Microsoft.EntityFrameworkCore.Query.Relationships; +using Microsoft.EntityFrameworkCore.Query.Relationships.ComplexProperties; using Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; using Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; @@ -30,11 +31,20 @@ public class InMemoryComplianceTest : ComplianceTestBase typeof(JsonQueryTestBase<>), typeof(AdHocJsonQueryTestBase), - // TODO: implement later once things are baked (#33985) + // Relationships tests - not implemented for InMemory typeof(RelationshipsProjectionTestBase<>), - typeof(OwnedNavigationsProjectionTestBase<>), + typeof(RelationshipsCollectionTestBase<>), + typeof(RelationshipsMiscellaneousTestBase<>), typeof(NavigationsIncludeTestBase<>), - typeof(NavigationsProjectionTestBase<>) + typeof(NavigationsProjectionTestBase<>), + typeof(NavigationsCollectionTestBase<>), + typeof(NavigationsMiscellaneousTestBase<>), + typeof(OwnedNavigationsProjectionTestBase<>), + typeof(OwnedNavigationsCollectionTestBase<>), + typeof(OwnedNavigationsMiscellaneousTestBase<>), + typeof(ComplexPropertiesProjectionTestBase<>), + typeof(ComplexPropertiesCollectionTestBase<>), + typeof(ComplexPropertiesMiscellaneousTestBase<>) }; protected override Assembly TargetAssembly { get; } = typeof(InMemoryComplianceTest).Assembly; diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexJson/ComplexJsonCollectionRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexJson/ComplexJsonCollectionRelationalTestBase.cs new file mode 100644 index 00000000000..629f66b0872 --- /dev/null +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexJson/ComplexJsonCollectionRelationalTestBase.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.EntityFrameworkCore.Query.Relationships.ComplexProperties; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexJson; + +public abstract class ComplexJsonCollectionRelationalTestBase : ComplexPropertiesCollectionTestBase + where TFixture : ComplexJsonRelationalFixtureBase, new() +{ + public ComplexJsonCollectionRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + protected void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexJson/ComplexJsonMiscellaneousRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexJson/ComplexJsonMiscellaneousRelationalTestBase.cs new file mode 100644 index 00000000000..314bd132141 --- /dev/null +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexJson/ComplexJsonMiscellaneousRelationalTestBase.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.EntityFrameworkCore.Query.Relationships.ComplexProperties; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexJson; + +public abstract class ComplexJsonMiscellaneousRelationalTestBase : ComplexPropertiesMiscellaneousTestBase + where TFixture : ComplexJsonRelationalFixtureBase, new() +{ + public ComplexJsonMiscellaneousRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + protected void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexJson/ComplexJsonProjectionRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexJson/ComplexJsonProjectionRelationalTestBase.cs new file mode 100644 index 00000000000..e02db1b3b48 --- /dev/null +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexJson/ComplexJsonProjectionRelationalTestBase.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.EntityFrameworkCore.Query.Relationships.ComplexProperties; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexJson; + +public abstract class ComplexJsonProjectionRelationalTestBase : ComplexPropertiesProjectionTestBase + where TFixture : ComplexJsonRelationalFixtureBase, new() +{ + public ComplexJsonProjectionRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + protected void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexJson/ComplexJsonRelationalFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexJson/ComplexJsonRelationalFixtureBase.cs new file mode 100644 index 00000000000..d2bc66bc0c8 --- /dev/null +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexJson/ComplexJsonRelationalFixtureBase.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.EntityFrameworkCore.Query.Relationships.ComplexProperties; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexJson; + +public abstract class ComplexJsonRelationalFixtureBase : ComplexPropertiesFixtureBase, ITestSqlLoggerFactory +{ + protected override string StoreName => "ComplexJsonQueryTest"; + + // TODO: Temporary, until we have update pipeline support for complex JSON + protected override Task SeedAsync(PoolableDbContext context) + => throw new NotImplementedException("Must be implemented in derived provider implementations using SQL, since the update pipeline does not yet support complex JSON."); + + protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) + { + base.OnModelCreating(modelBuilder, context); + + modelBuilder.Entity(b => + { + b.ComplexProperty(e => e.RequiredRelated, rrb => rrb.ToJson()); + b.ComplexProperty(e => e.OptionalRelated, orb => orb.ToJson()); + b.ComplexCollection(e => e.RelatedCollection, rcb => rcb.ToJson()); + }); + } + + public TestSqlLoggerFactory TestSqlLoggerFactory + => (TestSqlLoggerFactory)ListLoggerFactory; +} diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingMiscellaneousRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingMiscellaneousRelationalTestBase.cs new file mode 100644 index 00000000000..6e8269ea31a --- /dev/null +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingMiscellaneousRelationalTestBase.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.EntityFrameworkCore.Query.Relationships.ComplexProperties; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexTableSplitting; + +public abstract class ComplexTableSplittingMiscellaneousRelationalTestBase : ComplexPropertiesMiscellaneousTestBase + where TFixture : ComplexTableSplittingRelationalFixtureBase, new() +{ + public ComplexTableSplittingMiscellaneousRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + fixture.TestSqlLoggerFactory.Clear(); + fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + // TODO: Optional complex properties not yet supported (#31376) + public override Task Where_optional_related_property(bool async) + => AssertTranslationFailed(() => base.Where_optional_related_property(async)); + + protected void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingProjectionRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingProjectionRelationalTestBase.cs index 18a151934db..455bf2246c6 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingProjectionRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingProjectionRelationalTestBase.cs @@ -1,107 +1,53 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; +using Xunit.Sdk; namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexTableSplitting; -public abstract class ComplexTableSplittingProjectionRelationalTestBase(TFixture fixture) - : RelationshipsProjectionTestBase(fixture) +public abstract class ComplexTableSplittingProjectionRelationalTestBase + : RelationshipsProjectionTestBase where TFixture : ComplexTableSplittingRelationalFixtureBase, new() { - [ConditionalTheory] - [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_everything(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertQuery( - async, - ss => from r in ss.Set() - join t in ss.Set() on r.Id equals t.Id - select new { r, t }, - elementSorter: e => e.r.Id, - elementAsserter: (e, a) => - { - AssertEqual(e.r, a.r); - AssertEqual(e.t, a.t); - }, - queryTrackingBehavior: queryTrackingBehavior); - - [ConditionalTheory(Skip = "Complex collections not yet supported, #31237")] - public override Task Select_trunk_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - => base.Select_trunk_collection(async, queryTrackingBehavior); - - [ConditionalTheory(Skip = "Complex types projected via optional navigations, #31412")] - public override Task Select_branch_required_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) - => base.Select_branch_required_optional(async, queryTrackingBehavior); - - [ConditionalTheory(Skip = "Complex types projected via optional navigations, #31412")] - public override Task Select_branch_optional_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) - => base.Select_branch_optional_optional(async, queryTrackingBehavior); - - [ConditionalTheory(Skip = "Complex collections not yet supported, #31237")] - public override Task Select_branch_required_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - => base.Select_branch_required_collection(async, queryTrackingBehavior); - - [ConditionalTheory(Skip = "Complex collections not yet supported, #31237")] - public override Task Select_branch_optional_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - => base.Select_branch_optional_collection(async, queryTrackingBehavior); - - [ConditionalTheory(Skip = "Complex types projected via optional navigations, #31412")] - public override Task Select_branch_optional_required(bool async, QueryTrackingBehavior queryTrackingBehavior) - => base.Select_branch_optional_required(async, queryTrackingBehavior); - - #region Multiple - - [ConditionalTheory(Skip = "Complex types projected via optional navigations, #31412")] - public override Task Select_trunk_and_branch_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) - => base.Select_trunk_and_branch_duplicated(async, queryTrackingBehavior); - - [ConditionalTheory(Skip = "Complex types projected via optional navigations, #31412")] - public override Task Select_trunk_and_trunk_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) - => base.Select_trunk_and_trunk_duplicated(async, queryTrackingBehavior); - - [ConditionalTheory(Skip = "Complex collections not yet supported, #31237")] - public override Task Select_multiple_branch_leaf(bool async, QueryTrackingBehavior queryTrackingBehavior) - => base.Select_multiple_branch_leaf(async, queryTrackingBehavior); - - #endregion Multiple - - #region Subquery - - [ConditionalTheory(Skip = "Complex types projected via optional navigations, #31412")] - public override Task Select_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) - => base.Select_subquery_root_set_required_trunk_FirstOrDefault_branch(async, queryTrackingBehavior); - - [ConditionalTheory(Skip = "Complex types projected via optional navigations, #31412")] - public override Task Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) - => base.Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(async, queryTrackingBehavior); - - [ConditionalTheory(Skip = "Complex collections not yet supported, #31237")] - public override Task Select_subquery_root_set_trunk_FirstOrDefault_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - => base.Select_subquery_root_set_trunk_FirstOrDefault_collection(async, queryTrackingBehavior); - - [ConditionalTheory(Skip = "Complex collections not yet supported, #31237")] - public override Task Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) - => base.Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(async, queryTrackingBehavior); - - [ConditionalTheory(Skip = "Complex collections not yet supported, #31237")] - public override Task Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(bool async, QueryTrackingBehavior queryTrackingBehavior) - => base.Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(async, queryTrackingBehavior); - - #endregion Subquery - - #region SelectMany - - [ConditionalTheory(Skip = "Complex collections not yet supported, #31237")] - public override Task SelectMany_trunk_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - => base.SelectMany_trunk_collection(async, queryTrackingBehavior); - - [ConditionalTheory(Skip = "Complex collections not yet supported, #31237")] - public override Task SelectMany_required_trunk_reference_branch_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - => base.SelectMany_required_trunk_reference_branch_collection(async, queryTrackingBehavior); - - [ConditionalTheory(Skip = "Complex collections not yet supported, #31237")] - public override Task SelectMany_optional_trunk_reference_branch_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - => base.SelectMany_optional_trunk_reference_branch_collection(async, queryTrackingBehavior); - - #endregion SelectMany + public ComplexTableSplittingProjectionRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + // Optional complex types, #31376 + public override Task Select_optional_related_property_value_type(bool async, QueryTrackingBehavior queryTrackingBehavior) + => Assert.ThrowsAsync(() => base.Select_optional_related_property_value_type(async, queryTrackingBehavior)); + + // Complex JSON collections, update pipeline not yet supported so no seeding, #31237 + public override Task Select_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + => Assert.ThrowsAsync(() => base.Select_related_collection(async, queryTrackingBehavior)); + + // Complex JSON collections, update pipeline not yet supported so no seeding, #31237 + public override Task Select_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + => Assert.ThrowsAsync(() => base.Select_required_related_nested_collection(async, queryTrackingBehavior)); + + // Optional complex types, #31376 + public override Task Select_required_related_optional_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) + => Assert.ThrowsAsync(() => base.Select_required_related_optional_nested(async, queryTrackingBehavior)); + + // Optional complex types, #31376 + public override Task Select_subquery_required_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + => Assert.ThrowsAsync(() => base.Select_subquery_required_related_FirstOrDefault(async, queryTrackingBehavior)); + + // Complex JSON collections, update pipeline not yet supported so no seeding, #31237 + public override Task SelectMany_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + => Assert.ThrowsAsync(() => base.SelectMany_related_collection(async, queryTrackingBehavior)); + + // Optional complex types, #31376 + public override Task SelectMany_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + => Assert.ThrowsAsync(() => base.SelectMany_required_related_nested_collection(async, queryTrackingBehavior)); + + // Optional complex types, #31376 + public override Task SelectMany_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + => Assert.ThrowsAsync(() => base.SelectMany_optional_related_nested_collection(async, queryTrackingBehavior)); + + protected void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingRelationalFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingRelationalFixtureBase.cs index eec0360facb..71a8c69dc01 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingRelationalFixtureBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingRelationalFixtureBase.cs @@ -1,185 +1,63 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; +using Microsoft.EntityFrameworkCore.Query.Relationships.ComplexProperties; namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexTableSplitting; -public abstract class ComplexTableSplittingRelationalFixtureBase : RelationshipsQueryFixtureBase +/// +/// Base fixture for tests exercising table splitting, where the entity and its contained complex type are mapped to the same +/// table, and the complex type's properties are mapped to columns in that table. +/// +/// +/// Note that collections aren't supported with table splitting, so this fixture ignores them in the model configuration and +/// removes them from the seeding data. +/// +public abstract class ComplexTableSplittingRelationalFixtureBase : ComplexPropertiesFixtureBase, ITestSqlLoggerFactory { protected override string StoreName => "ComplexTableSplittingQueryTest"; - protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) + protected override RelationshipsData CreateData() { - base.OnModelCreating(modelBuilder, context); - - modelBuilder.Entity().ToTable("RootEntities"); - modelBuilder.Entity().ToTable("TrunkEntities"); - modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); - - modelBuilder.Entity() - .HasOne(x => x.OptionalReferenceTrunk) - .WithOne(x => x.OptionalReferenceInverseRoot) - .HasForeignKey(x => x.OptionalReferenceTrunkId) - .IsRequired(false); - - // TODO: Why is this a navigation and not a complex property? - modelBuilder.Entity() - .HasOne(x => x.RequiredReferenceTrunk) - .WithOne(x => x.RequiredReferenceInverseRoot) - .HasForeignKey(x => x.RequiredReferenceTrunkId) - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(true); - - modelBuilder.Entity().Ignore(x => x.CollectionTrunk); - modelBuilder.Entity().Ignore(x => x.CollectionInverseRoot); - - // TODO: issue #31376 - complex optional references - modelBuilder.Entity() - .Ignore(x => x.OptionalReferenceBranch); - - modelBuilder.Entity() - .ComplexProperty(x => x.RequiredReferenceBranch, bb => - { - bb.IsRequired(true); - bb.Ignore(x => x.Id); - bb.Ignore(x => x.OptionalReferenceInverseTrunk); - bb.Ignore(x => x.OptionalReferenceLeafId); - - // TODO: issue #31376 - complex optional references - bb.Ignore(x => x.OptionalReferenceLeaf); - - bb.Ignore(x => x.RequiredReferenceInverseTrunk); - bb.Ignore(x => x.RequiredReferenceLeafId); - bb.ComplexProperty(x => x.RequiredReferenceLeaf, bbb => - { - bbb.IsRequired(true); - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); + // Collections are not supported with table splitting, only with JSON + var data = new RelationshipsData(withCollections: false); - bb.Ignore(x => x.CollectionInverseTrunk); - bb.Ignore(x => x.CollectionTrunkId); - bb.Ignore(x => x.CollectionLeaf); - }); - - // collections are not supported for non-json compex types - modelBuilder.Entity().Ignore(x => x.CollectionBranch); - } - - protected override Task SeedAsync(RelationshipsContext context) - { - var rootEntities = RelationshipsData.CreateRootEntities(); - var trunkEntities = RelationshipsData.CreateTrunkEntitiesWithOwnerships(); - - RelationshipsData.WireUp(rootEntities, trunkEntities, [], [], wireUpRootToTrunkOnly: true); - - context.Set().AddRange(rootEntities); - context.Set().AddRange(trunkEntities); + // TODO: Optional complex properties not yet supported (#31376), remove them from the seeding data + foreach (var rootEntity in data.RootEntities) + { + rootEntity.OptionalRelated = null; + rootEntity.RequiredRelated.OptionalNested = null; + } - return context.SaveChangesAsync(); + return data; } - public override IReadOnlyDictionary EntitySorters { get; } = new Dictionary> - { - { typeof(RelationshipsRoot), e => ((RelationshipsRoot?)e)?.Id }, - { typeof(RelationshipsTrunk), e => ((RelationshipsTrunk?)e)?.Id }, - { typeof(RelationshipsBranch), e => ((RelationshipsBranch?)e)?.Name }, - { typeof(RelationshipsLeaf), e => ((RelationshipsLeaf?)e)?.Name } - }.ToDictionary(e => e.Key, e => (object)e.Value); - - public override IReadOnlyDictionary EntityAsserters { get; } = new Dictionary> + protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) { - { - typeof(RelationshipsRoot), (e, a) => - { - Assert.Equal(e == null, a == null); - - if (a != null) - { - var ee = (RelationshipsRoot)e!; - var aa = (RelationshipsRoot)a; - - Assert.Equal(ee.Id, aa.Id); - - Assert.Equal(ee.Name, aa.Name); - } - } - }, - { - typeof(RelationshipsTrunk), (e, a) => - { - Assert.Equal(e == null, a == null); - - if (a != null) - { - var ee = (RelationshipsTrunk)e!; - var aa = (RelationshipsTrunk)a; - - Assert.Equal(ee.Id, aa.Id); - - Assert.Equal(ee.Name, aa.Name); - - AssertOwnedBranch(ee.RequiredReferenceBranch, aa.RequiredReferenceBranch); - - // TODO: issue #31376 - complex optional references - //Assert.Equal(ee.OptionalReferenceBranch == null, aa.OptionalReferenceBranch == null); - - if (ee.OptionalReferenceBranch != null && aa.OptionalReferenceBranch != null) - { - AssertOwnedBranch(ee.OptionalReferenceBranch, aa.OptionalReferenceBranch); - } - } - } - }, - { - typeof(RelationshipsBranch), (e, a) => - { - Assert.Equal(e == null, a == null); + base.OnModelCreating(modelBuilder, context); - if (a != null) - { - var ee = (RelationshipsBranch)e!; - var aa = (RelationshipsBranch)a; - AssertOwnedBranch(ee, aa); - } - } - }, + modelBuilder.Entity(b => { - typeof(RelationshipsLeaf), (e, a) => + b.ComplexProperty(e => e.RequiredRelated, rrb => { - Assert.Equal(e == null, a == null); + rrb.ComplexProperty(r => r.RequiredNested); - if (a != null) - { - var ee = (RelationshipsLeaf)e!; - var aa = (RelationshipsLeaf)a; - AssertOwnedLeaf(ee, aa); - } - } - }, - }.ToDictionary(e => e.Key, e => (object)e.Value); + // TODO: Optional complex properties not yet supported: #31376 + rrb.Ignore(r => r.OptionalNested); - public static void AssertOwnedBranch(RelationshipsBranch expected, RelationshipsBranch actual) - { - Assert.Equal(expected.Name, actual.Name); + // Collections are not supported with table splitting, only JSON + rrb.Ignore(r => r.NestedCollection); + }); - AssertOwnedLeaf(expected.RequiredReferenceLeaf, actual.RequiredReferenceLeaf); + // TODO: Optional complex properties not yet supported: #31376 + b.Ignore(r => r.OptionalRelated); - // TODO: issue #31376 - complex optional references - //Assert.Equal(expected.OptionalReferenceLeaf == null, actual.OptionalReferenceLeaf == null); - //if (expected.OptionalReferenceLeaf != null && actual.OptionalReferenceLeaf != null) - //{ - // AssertOwnedLeaf(expected.OptionalReferenceLeaf, actual.OptionalReferenceLeaf); - //} + // Collections are not supported with table splitting, only JSON + b.Ignore(r => r.RelatedCollection); + }); } - public static void AssertOwnedLeaf(RelationshipsLeaf expected, RelationshipsLeaf actual) - { - Assert.Equal(expected.Name, actual.Name); - } + public TestSqlLoggerFactory TestSqlLoggerFactory + => (TestSqlLoggerFactory)ListLoggerFactory; } diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/JsonOwnedNavigations/JsonOwnedNavigationsProjectionRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/JsonOwnedNavigations/JsonOwnedNavigationsProjectionRelationalTestBase.cs deleted file mode 100644 index 5688e7583ce..00000000000 --- a/test/EFCore.Relational.Specification.Tests/Query/Relationships/JsonOwnedNavigations/JsonOwnedNavigationsProjectionRelationalTestBase.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; - -namespace Microsoft.EntityFrameworkCore.Query.Relationships.JsonOwnedNavigations; - -public abstract class JsonOwnedNavigationsProjectionRelationalTestBase(TFixture fixture) - : OwnedNavigationsProjectionTestBase(fixture) - where TFixture : JsonOwnedNavigationsRelationalFixtureBase, new() -{ -} diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/JsonOwnedNavigations/JsonOwnedNavigationsRelationalFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/JsonOwnedNavigations/JsonOwnedNavigationsRelationalFixtureBase.cs deleted file mode 100644 index 4f951a3a466..00000000000 --- a/test/EFCore.Relational.Specification.Tests/Query/Relationships/JsonOwnedNavigations/JsonOwnedNavigationsRelationalFixtureBase.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; -using Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; - -namespace Microsoft.EntityFrameworkCore.Query.Relationships.JsonOwnedNavigations; - -public abstract class JsonOwnedNavigationsRelationalFixtureBase : OwnedNavigationsFixtureBase -{ - protected override string StoreName => "OwnedNavigationsJsonQueryTest"; - - protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) - { - base.OnModelCreating(modelBuilder, context); - - modelBuilder.Entity().ToTable("RootEntities"); - modelBuilder.Entity().OwnsOne(x => x.OptionalReferenceTrunk).ToJson(); - modelBuilder.Entity().OwnsOne(x => x.RequiredReferenceTrunk).ToJson(); - modelBuilder.Entity().OwnsMany(x => x.CollectionTrunk).ToJson(); - } -} diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/Navigations/NavigationsCollectionRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/Navigations/NavigationsCollectionRelationalTestBase.cs new file mode 100644 index 00000000000..07c950280d8 --- /dev/null +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/Navigations/NavigationsCollectionRelationalTestBase.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; + +public abstract class NavigationsCollectionRelationalTestBase : NavigationsCollectionTestBase + where TFixture : NavigationsRelationalFixtureBase, new() +{ + public NavigationsCollectionRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + protected void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/Navigations/NavigationsIncludeRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/Navigations/NavigationsIncludeRelationalTestBase.cs index 6b419fa4065..6888eda540d 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/Relationships/Navigations/NavigationsIncludeRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/Navigations/NavigationsIncludeRelationalTestBase.cs @@ -3,8 +3,16 @@ namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; -public abstract class NavigationsIncludeRelationalTestBase(TFixture fixture) - : NavigationsIncludeTestBase(fixture) - where TFixture : NavigationsRelationalFixtureBase, new() +public abstract class NavigationsIncludeRelationalTestBase : NavigationsIncludeTestBase + where TFixture : NavigationsRelationalFixtureBase, new() { + public NavigationsIncludeRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + protected void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/Navigations/NavigationsMiscellaneousRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/Navigations/NavigationsMiscellaneousRelationalTestBase.cs new file mode 100644 index 00000000000..ad7a8e94672 --- /dev/null +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/Navigations/NavigationsMiscellaneousRelationalTestBase.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; + +public abstract class NavigationsMiscellaneousRelationalTestBase : NavigationsMiscellaneousTestBase + where TFixture : NavigationsRelationalFixtureBase, new() +{ + public NavigationsMiscellaneousRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + fixture.TestSqlLoggerFactory.Clear(); + fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + protected void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/Navigations/NavigationsProjectionRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/Navigations/NavigationsProjectionRelationalTestBase.cs index da67784b16c..292753da236 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/Relationships/Navigations/NavigationsProjectionRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/Navigations/NavigationsProjectionRelationalTestBase.cs @@ -3,8 +3,17 @@ namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; -public abstract class NavigationsProjectionRelationalTestBase(TFixture fixture) - : NavigationsProjectionTestBase(fixture) +public abstract class NavigationsProjectionRelationalTestBase + : NavigationsProjectionTestBase where TFixture : NavigationsRelationalFixtureBase, new() { + public NavigationsProjectionRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + protected void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/Navigations/NavigationsRelationalFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/Navigations/NavigationsRelationalFixtureBase.cs index 3b958abd8f3..bee3c790e78 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/Relationships/Navigations/NavigationsRelationalFixtureBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/Navigations/NavigationsRelationalFixtureBase.cs @@ -1,19 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; - namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; -public abstract class NavigationsRelationalFixtureBase : NavigationsFixtureBase +public abstract class NavigationsRelationalFixtureBase : NavigationsFixtureBase, ITestSqlLoggerFactory { - protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) - { - base.OnModelCreating(modelBuilder, context); + protected override string StoreName => "NavigationsQueryTest"; - modelBuilder.Entity().ToTable("RootEntities"); - modelBuilder.Entity().ToTable("TrunkEntities"); - modelBuilder.Entity().ToTable("BranchEntities"); - modelBuilder.Entity().ToTable("LeafEntities"); - } + public TestSqlLoggerFactory TestSqlLoggerFactory + => (TestSqlLoggerFactory)ListLoggerFactory; } diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedJson/OwnedJsonCollectionRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedJson/OwnedJsonCollectionRelationalTestBase.cs new file mode 100644 index 00000000000..ca50999ddc5 --- /dev/null +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedJson/OwnedJsonCollectionRelationalTestBase.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedJson; + +public abstract class OwnedJsonCollectionRelationalTestBase : OwnedNavigationsCollectionTestBase + where TFixture : OwnedJsonRelationalFixtureBase, new() +{ + public OwnedJsonCollectionRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + protected void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedJson/OwnedJsonMiscellaneousRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedJson/OwnedJsonMiscellaneousRelationalTestBase.cs new file mode 100644 index 00000000000..fa79eba5c16 --- /dev/null +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedJson/OwnedJsonMiscellaneousRelationalTestBase.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedJson; + +public abstract class OwnedJsonMiscellaneousRelationalTestBase : OwnedNavigationsMiscellaneousTestBase + where TFixture : OwnedJsonRelationalFixtureBase, new() +{ + public OwnedJsonMiscellaneousRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + fixture.TestSqlLoggerFactory.Clear(); + fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + protected void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedJson/OwnedJsonProjectionRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedJson/OwnedJsonProjectionRelationalTestBase.cs new file mode 100644 index 00000000000..46c5807dd33 --- /dev/null +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedJson/OwnedJsonProjectionRelationalTestBase.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedJson; + +public abstract class OwnedJsonProjectionRelationalTestBase : OwnedNavigationsProjectionTestBase + where TFixture : OwnedJsonRelationalFixtureBase, new() +{ + public OwnedJsonProjectionRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + fixture.TestSqlLoggerFactory.Clear(); + fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + protected void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedJson/OwnedJsonRelationalFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedJson/OwnedJsonRelationalFixtureBase.cs new file mode 100644 index 00000000000..2eff32f9c46 --- /dev/null +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedJson/OwnedJsonRelationalFixtureBase.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedJson; + +public abstract class OwnedJsonRelationalFixtureBase : OwnedNavigationsFixtureBase, ITestSqlLoggerFactory +{ + protected override string StoreName => "OwnedJsonJsonQueryTest"; + + protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) + { + base.OnModelCreating(modelBuilder, context); + + modelBuilder.Entity(b => + { + b.OwnsOne(e => e.RequiredRelated, rrb => rrb.ToJson()); + b.OwnsOne(e => e.OptionalRelated, orb => orb.ToJson()); + b.OwnsMany(e => e.RelatedCollection, orb => orb.ToJson()); + }); + } + + public TestSqlLoggerFactory TestSqlLoggerFactory + => (TestSqlLoggerFactory)ListLoggerFactory; +} diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsCollectionRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsCollectionRelationalTestBase.cs new file mode 100644 index 00000000000..765dc75136e --- /dev/null +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsCollectionRelationalTestBase.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; + +public abstract class OwnedNavigationsCollectionRelationalTestBase : OwnedNavigationsCollectionTestBase + where TFixture : OwnedNavigationsRelationalFixtureBase, new() +{ + public OwnedNavigationsCollectionRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsMiscellaneousRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsMiscellaneousRelationalTestBase.cs new file mode 100644 index 00000000000..dd1196504cb --- /dev/null +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsMiscellaneousRelationalTestBase.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; + +public abstract class OwnedNavigationsMiscellaneousRelationalTestBase : OwnedNavigationsMiscellaneousTestBase + where TFixture : OwnedNavigationsRelationalFixtureBase, new() +{ + public OwnedNavigationsMiscellaneousRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + fixture.TestSqlLoggerFactory.Clear(); + fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionRelationalTestBase.cs index 9717eed8154..1fe8a165166 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionRelationalTestBase.cs @@ -3,8 +3,17 @@ namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; -public abstract class OwnedNavigationsProjectionRelationalTestBase(TFixture fixture) - : OwnedNavigationsProjectionTestBase(fixture) +public abstract class OwnedNavigationsProjectionRelationalTestBase + : OwnedNavigationsProjectionTestBase where TFixture : OwnedNavigationsRelationalFixtureBase, new() { + public OwnedNavigationsProjectionRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + public void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsRelationalFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsRelationalFixtureBase.cs index e6bb002138e..40dcf9e126f 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsRelationalFixtureBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsRelationalFixtureBase.cs @@ -1,11 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; +using Microsoft.EntityFrameworkCore.Query.Relationships.OwnedTableSplitting; namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; -public abstract class OwnedNavigationsRelationalFixtureBase : OwnedNavigationsFixtureBase +/// +/// Base fixture for tests exercising owned entities mapped to separate tables. +/// +public abstract class OwnedNavigationsRelationalFixtureBase : OwnedTableSplittingRelationalFixtureBase, ITestSqlLoggerFactory { protected override string StoreName => "OwnedNavigationsQueryTest"; @@ -13,98 +16,34 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con { base.OnModelCreating(modelBuilder, context); - modelBuilder.Entity().ToTable("RootEntities"); - modelBuilder.Entity() - .OwnsOne(x => x.OptionalReferenceTrunk, b => + // Note that this fixture extends OwnedTableSplittingRelationalFixtureBase; it only overrides to + // map the non-collection owned navigations to separate tables (disabling the default table splitting behavior). + modelBuilder.Entity(b => + { + b.OwnsOne(e => e.RequiredRelated, rrb => { - b.OwnsOne(x => x.OptionalReferenceBranch, bb => - { - bb.OwnsMany(x => x.CollectionLeaf, bbb => - { - bbb.ToTable("Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf"); - }); - }); + rrb.ToTable("RequiredRelated"); - b.OwnsOne(x => x.RequiredReferenceBranch, bb => - { - bb.OwnsMany(x => x.CollectionLeaf, bbb => - { - bbb.ToTable("Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf"); - }); - }); - - b.OwnsMany(x => x.CollectionBranch, bb => - { - bb.ToTable("Root_OptionalReferenceTrunk_CollectionBranch"); - - bb.OwnsMany(x => x.CollectionLeaf, bbb => - { - bbb.ToTable("Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf"); - }); - }); + rrb.OwnsOne(r => r.RequiredNested, rnb => rnb.ToTable("RequiredRelated_RequiredNested")); + rrb.OwnsOne(r => r.OptionalNested, rnb => rnb.ToTable("RequiredRelated_OptionalNested")); }); - modelBuilder.Entity() - .OwnsOne(x => x.RequiredReferenceTrunk, b => + b.OwnsOne(e => e.OptionalRelated, rrb => { - b.OwnsOne(x => x.OptionalReferenceBranch, bb => - { - bb.OwnsMany(x => x.CollectionLeaf, bbb => - { - bbb.ToTable("Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf"); - }); - }); - - b.OwnsOne(x => x.RequiredReferenceBranch, bb => - { - bb.OwnsMany(x => x.CollectionLeaf, bbb => - { - bbb.ToTable("Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf"); - }); - }); + rrb.ToTable("OptionalRelated"); - b.OwnsMany(x => x.CollectionBranch, bb => - { - bb.ToTable("Root_RequiredReferenceTrunk_CollectionBranch"); - - bb.OwnsMany(x => x.CollectionLeaf, bbb => - { - bbb.ToTable("Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf"); - }); - }); + rrb.OwnsOne(r => r.RequiredNested, rnb => rnb.ToTable("OptionalRelated_RequiredNested")); + rrb.OwnsOne(r => r.OptionalNested, rnb => rnb.ToTable("OptionalRelated_OptionalNested")); }); - modelBuilder.Entity().Navigation(x => x.RequiredReferenceTrunk).IsRequired(true); - modelBuilder.Entity() - .OwnsMany(x => x.CollectionTrunk, b => + b.OwnsMany(e => e.RelatedCollection, rcb => { - b.ToTable("Root_CollectionTrunk"); - - b.OwnsOne(x => x.OptionalReferenceBranch, bb => - { - bb.OwnsMany(x => x.CollectionLeaf, bbb => - { - bbb.ToTable("Root_CollectionTrunk_OptionalReferenceBranch_CollectionLeaf"); - }); - }); - - b.OwnsOne(x => x.RequiredReferenceBranch, bb => - { - bb.OwnsMany(x => x.CollectionLeaf, bbb => - { - bbb.ToTable("Root_CollectionTrunk_RequiredReferenceBranch_CollectionLeaf"); - }); - }); - - b.OwnsMany(x => x.CollectionBranch, bb => - { - bb.ToTable("Root_CollectionTrunk_CollectionBranch"); - - bb.OwnsMany(x => x.CollectionLeaf, bbb => - { - bbb.ToTable("Root_CollectionTrunk_CollectionBranch_CollectionLeaf"); - }); - }); + rcb.OwnsOne(r => r.RequiredNested, rnb => rnb.ToTable("RelatedCollection_RequiredNested")); + rcb.OwnsOne(r => r.OptionalNested, rnb => rnb.ToTable("RelatedCollection_OptionalNested")); }); + }); } + + public void AssertSql(params string[] expected) + => TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingMiscellaneousRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingMiscellaneousRelationalTestBase.cs new file mode 100644 index 00000000000..cd3f495e676 --- /dev/null +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingMiscellaneousRelationalTestBase.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedTableSplitting; + +public abstract class OwnedTableSplittingMiscellaneousRelationalTestBase : OwnedNavigationsMiscellaneousTestBase + where TFixture : OwnedTableSplittingRelationalFixtureBase, new() +{ + public OwnedTableSplittingMiscellaneousRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + fixture.TestSqlLoggerFactory.Clear(); + fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + protected void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); +} diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingProjectionRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingProjectionRelationalTestBase.cs index 706708be965..bb15d0bb5f8 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingProjectionRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingProjectionRelationalTestBase.cs @@ -2,11 +2,29 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; +using Xunit.Sdk; namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedTableSplitting; -public abstract class OwnedTableSplittingProjectionRelationalTestBase(TFixture fixture) - : OwnedNavigationsProjectionTestBase(fixture) +public abstract class OwnedTableSplittingProjectionRelationalTestBase + : OwnedNavigationsProjectionTestBase where TFixture : OwnedTableSplittingRelationalFixtureBase, new() { + public OwnedTableSplittingProjectionRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + Fixture.TestSqlLoggerFactory.Clear(); + Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); + } + + // // The following are tests projecting out collections, which aren't supported with table splitting. + // // Collection properties are ignored in the model, and since these tests project we client-eval and get an assertion failure. + // public override Task Select_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + // => Assert.ThrowsAsync(() => base.Select_related_collection(async, queryTrackingBehavior)); + + // public override Task Select_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + // => Assert.ThrowsAsync(() => base.Select_optional_related_nested_collection(async, queryTrackingBehavior)); + + protected void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingRelationalFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingRelationalFixtureBase.cs index 024945ac7b2..2e947970a7a 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingRelationalFixtureBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingRelationalFixtureBase.cs @@ -2,11 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; -using Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedTableSplitting; -public abstract class OwnedTableSplittingRelationalFixtureBase : OwnedNavigationsFixtureBase +/// +/// Base fixture for tests exercising table splitting via owned entities, where the entity and its owned entity are mapped to the same +/// table, and the complex type's properties are mapped to columns in that table. Collections are mapped to separate tables. +/// +public abstract class OwnedTableSplittingRelationalFixtureBase : OwnedNavigationsFixtureBase, ITestSqlLoggerFactory { protected override string StoreName => "OwnedTableSplittingQueryTest"; @@ -14,78 +17,64 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con { base.OnModelCreating(modelBuilder, context); - modelBuilder.Entity() - .OwnsOne(x => x.OptionalReferenceTrunk, b => + modelBuilder.Entity(b => + { + b.OwnsOne(e => e.RequiredRelated, rrb => { - b.ToTable("Root_OptionalReferenceTrunk"); - b.OwnsOne(x => x.OptionalReferenceBranch, bb => - { - bb.ToTable("Root_OptionalReferenceTrunk_OptionalReferenceBranch"); - bb.OwnsOne(x => x.RequiredReferenceLeaf).ToTable("Root_OptionalReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf"); - bb.OwnsOne(x => x.OptionalReferenceLeaf).ToTable("Root_OptionalReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf"); - }); + rrb.Property(x => x.Id).ValueGeneratedNever(); - b.OwnsOne(x => x.RequiredReferenceBranch, bb => - { - bb.ToTable("Root_OptionalReferenceTrunk_RequiredReferenceBranch"); - bb.OwnsOne(x => x.RequiredReferenceLeaf).ToTable("Root_OptionalReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf"); - bb.OwnsOne(x => x.OptionalReferenceLeaf).ToTable("Root_OptionalReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf"); - }); + rrb.OwnsOne(r => r.RequiredNested, rnb => rnb.Property(x => x.Id).ValueGeneratedNever()); + rrb.Navigation(x => x.RequiredNested).IsRequired(true); - b.OwnsMany(x => x.CollectionBranch, bb => + rrb.OwnsOne(r => r.OptionalNested, rnb => rnb.Property(x => x.Id).ValueGeneratedNever()); + rrb.Navigation(x => x.RequiredNested).IsRequired(false); + + rrb.OwnsMany(r => r.NestedCollection, rcb => { - bb.OwnsOne(x => x.RequiredReferenceLeaf).ToTable("Root_OptionalReferenceTrunk_CollectionBranch_RequiredReferenceLeaf"); - bb.OwnsOne(x => x.OptionalReferenceLeaf).ToTable("Root_OptionalReferenceTrunk_CollectionBranch_OptionalReferenceLeaf"); + rcb.Property(x => x.Id).ValueGeneratedNever(); + rcb.ToTable("RequiredRelated_NestedCollection"); }); }); + b.Navigation(x => x.RequiredRelated).IsRequired(true); - modelBuilder.Entity() - .OwnsOne(x => x.RequiredReferenceTrunk, b => + b.OwnsOne(e => e.OptionalRelated, orb => { - b.ToTable("Root_RequiredReferenceTrunk"); - b.OwnsOne(x => x.OptionalReferenceBranch, bb => - { - bb.ToTable("Root_RequiredReferenceTrunk_OptionalReferenceBranch"); - bb.OwnsOne(x => x.RequiredReferenceLeaf).ToTable("Root_RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf"); - bb.OwnsOne(x => x.OptionalReferenceLeaf).ToTable("Root_RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf"); - }); + orb.Property(x => x.Id).ValueGeneratedNever(); - b.OwnsOne(x => x.RequiredReferenceBranch, bb => - { - bb.ToTable("Root_RequiredReferenceTrunk_RequiredReferenceBranch"); - bb.OwnsOne(x => x.RequiredReferenceLeaf).ToTable("Root_RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf"); - bb.OwnsOne(x => x.OptionalReferenceLeaf).ToTable("Root_RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf"); - }); + orb.OwnsOne(r => r.RequiredNested, rnb => rnb.Property(x => x.Id).ValueGeneratedNever()); + orb.Navigation(x => x.RequiredNested).IsRequired(true); + + orb.OwnsOne(r => r.OptionalNested, rnb => rnb.Property(x => x.Id).ValueGeneratedNever()); + orb.Navigation(x => x.RequiredNested).IsRequired(false); - b.OwnsMany(x => x.CollectionBranch, bb => + orb.OwnsMany(r => r.NestedCollection, rcb => { - bb.OwnsOne(x => x.RequiredReferenceLeaf).ToTable("Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf"); - bb.OwnsOne(x => x.OptionalReferenceLeaf).ToTable("Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf"); + rcb.Property(x => x.Id).ValueGeneratedNever(); + rcb.ToTable("OptionalRelated_NestedCollection"); }); }); + b.Navigation(x => x.OptionalRelated).IsRequired(false); - modelBuilder.Entity() - .OwnsMany(x => x.CollectionTrunk, b => + b.OwnsMany(e => e.RelatedCollection, rcb => { - b.OwnsOne(x => x.OptionalReferenceBranch, bb => - { - bb.ToTable("Root_CollectionTrunk_OptionalReferenceBranch"); - bb.OwnsOne(x => x.RequiredReferenceLeaf).ToTable("Root_CollectionTrunk_OptionalReferenceBranch_RequiredReferenceLeaf"); - bb.OwnsOne(x => x.OptionalReferenceLeaf).ToTable("Root_CollectionTrunk_OptionalReferenceBranch_OptionalReferenceLeaf"); - }); + rcb.Property(x => x.Id).ValueGeneratedNever(); + rcb.ToTable("RelatedCollection"); - b.OwnsOne(x => x.RequiredReferenceBranch, bb => - { - bb.ToTable("Root_CollectionTrunk_RequiredReferenceBranch"); - bb.OwnsOne(x => x.RequiredReferenceLeaf).ToTable("Root_CollectionTrunk_RequiredReferenceBranch_RequiredReferenceLeaf"); - bb.OwnsOne(x => x.OptionalReferenceLeaf).ToTable("Root_CollectionTrunk_RequiredReferenceBranch_OptionalReferenceLeaf"); - }); + rcb.OwnsOne(r => r.RequiredNested, rnb => rnb.Property(x => x.Id).ValueGeneratedNever()); + rcb.Navigation(x => x.RequiredNested).IsRequired(true); - b.OwnsMany(x => x.CollectionBranch, bb => + rcb.OwnsOne(r => r.OptionalNested, rnb => rnb.Property(x => x.Id).ValueGeneratedNever()); + rcb.Navigation(x => x.RequiredNested).IsRequired(false); + + rcb.OwnsMany(r => r.NestedCollection, rcb => { - bb.OwnsOne(x => x.RequiredReferenceLeaf).ToTable("Root_CollectionTrunk_CollectionBranch_RequiredReferenceLeaf"); - bb.OwnsOne(x => x.OptionalReferenceLeaf).ToTable("Root_CollectionTrunk_CollectionBranch_OptionalReferenceLeaf"); + rcb.Property(x => x.Id).ValueGeneratedNever(); + rcb.ToTable("RelatedCollection_NestedCollection"); }); }); + }); } + + public TestSqlLoggerFactory TestSqlLoggerFactory + => (TestSqlLoggerFactory)ListLoggerFactory; } diff --git a/test/EFCore.Specification.Tests/MaterializationInterceptionTestBase.cs b/test/EFCore.Specification.Tests/MaterializationInterceptionTestBase.cs index 7f03146f1bd..aed7059b329 100644 --- a/test/EFCore.Specification.Tests/MaterializationInterceptionTestBase.cs +++ b/test/EFCore.Specification.Tests/MaterializationInterceptionTestBase.cs @@ -49,7 +49,7 @@ public virtual async Task Binding_interceptors_are_used_when_creating_instances( using var context = await CreateContext(interceptors, inject, usePooling); - var materializer = context.GetService(); + var materializer = context.GetService(); var book = (Book)materializer.GetEmptyMaterializer(context.Model.FindEntityType(typeof(Book))!)( new MaterializationContext(ValueBuffer.Empty, context)); diff --git a/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs index f0a69a06080..7248a7f96ac 100644 --- a/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs @@ -12,19 +12,19 @@ protected ComplexTypeQueryTestBase(TFixture fixture) : base(fixture) => fixture.ListLoggerFactory.Clear(); - [ConditionalTheory] - [MemberData(nameof(IsAsyncData))] - public virtual Task Filter_on_property_inside_complex_type(bool async) - => AssertQuery( - async, - ss => ss.Set().Where(c => c.ShippingAddress.ZipCode == 07728)); - - [ConditionalTheory] - [MemberData(nameof(IsAsyncData))] - public virtual Task Filter_on_property_inside_nested_complex_type(bool async) - => AssertQuery( - async, - ss => ss.Set().Where(c => c.ShippingAddress.Country.Code == "DE")); + // [ConditionalTheory] + // [MemberData(nameof(IsAsyncData))] + // public virtual Task Filter_on_property_inside_complex_type(bool async) + // => AssertQuery( + // async, + // ss => ss.Set().Where(c => c.ShippingAddress.ZipCode == 07728)); + + // [ConditionalTheory] + // [MemberData(nameof(IsAsyncData))] + // public virtual Task Filter_on_property_inside_nested_complex_type(bool async) + // => AssertQuery( + // async, + // ss => ss.Set().Where(c => c.ShippingAddress.Country.Code == "DE")); [ConditionalTheory] [MemberData(nameof(IsAsyncData))] diff --git a/test/EFCore.Specification.Tests/Query/Relationships/ComplexProperties/ComplexPropertiesCollectionTestBase.cs b/test/EFCore.Specification.Tests/Query/Relationships/ComplexProperties/ComplexPropertiesCollectionTestBase.cs new file mode 100644 index 00000000000..b016c121b23 --- /dev/null +++ b/test/EFCore.Specification.Tests/Query/Relationships/ComplexProperties/ComplexPropertiesCollectionTestBase.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit.Sdk; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexProperties; + +public abstract class ComplexPropertiesCollectionTestBase(TFixture fixture) + : RelationshipsCollectionTestBase(fixture) + where TFixture : ComplexPropertiesFixtureBase, new() +{ + // TODO: the following is temporary until change tracking is implemented for complex JSON types (#35962) + private readonly TrackingRewriter _trackingRewriter = new(QueryTrackingBehavior.NoTracking); + + protected override Expression RewriteServerQueryExpression(Expression serverQueryExpression) + { + var rewritten = _trackingRewriter.Visit(serverQueryExpression); + + return rewritten; + } +} diff --git a/test/EFCore.Specification.Tests/Query/Relationships/ComplexProperties/ComplexPropertiesFixtureBase.cs b/test/EFCore.Specification.Tests/Query/Relationships/ComplexProperties/ComplexPropertiesFixtureBase.cs new file mode 100644 index 00000000000..99caf2a92b6 --- /dev/null +++ b/test/EFCore.Specification.Tests/Query/Relationships/ComplexProperties/ComplexPropertiesFixtureBase.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexProperties; + +public abstract class ComplexPropertiesFixtureBase : RelationshipsQueryFixtureBase +{ + protected override string StoreName => "ComplexRelationshipsQueryTest"; + + protected override Task SeedAsync(PoolableDbContext context) + { + var rootEntities = RelationshipsData.CreateRootEntities(); + context.Set().AddRange(rootEntities); + + return context.SaveChangesAsync(); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) + { + base.OnModelCreating(modelBuilder, context); + + modelBuilder.Entity(b => + { + b.ComplexProperty(e => e.RequiredRelated, rrb => + { + rrb.ComplexProperty(r => r.RequiredNested); + rrb.ComplexProperty(r => r.OptionalNested); + rrb.ComplexCollection(r => r.NestedCollection); + }); + + b.ComplexProperty(e => e.OptionalRelated, orb => + { + orb.ComplexProperty(r => r.RequiredNested); + orb.ComplexProperty(r => r.OptionalNested); + orb.ComplexCollection(r => r.NestedCollection); + }); + + b.ComplexCollection(e => e.RelatedCollection, rcb => + { + rcb.ComplexProperty(r => r.RequiredNested); + rcb.ComplexProperty(r => r.OptionalNested); + rcb.ComplexCollection(r => r.NestedCollection); + }); + }); + } + + // Derived fixtures ignore some complex properties that are mapped in this one + // (e.g. complex table splitting does not support collections) + public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) + => builder.ConfigureWarnings(b => + b.Default(WarningBehavior.Ignore).Log(CoreEventId.MappedComplexPropertyIgnoredWarning)); +} + diff --git a/test/EFCore.Specification.Tests/Query/Relationships/ComplexProperties/ComplexPropertiesMiscellaneousTestBase.cs b/test/EFCore.Specification.Tests/Query/Relationships/ComplexProperties/ComplexPropertiesMiscellaneousTestBase.cs new file mode 100644 index 00000000000..6f5fe01ec1f --- /dev/null +++ b/test/EFCore.Specification.Tests/Query/Relationships/ComplexProperties/ComplexPropertiesMiscellaneousTestBase.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexProperties; + +public abstract class ComplexPropertiesMiscellaneousTestBase(TFixture fixture) + : RelationshipsMiscellaneousTestBase(fixture) + where TFixture : ComplexPropertiesFixtureBase, new() +{ + // TODO: the following is temporary until change tracking is implemented for complex JSON types (#35962) + private readonly TrackingRewriter _trackingRewriter = new(QueryTrackingBehavior.NoTracking); + + protected override Expression RewriteServerQueryExpression(Expression serverQueryExpression) + { + var rewritten = _trackingRewriter.Visit(serverQueryExpression); + + return rewritten; + } +} diff --git a/test/EFCore.Specification.Tests/Query/Relationships/ComplexProperties/ComplexPropertiesProjectionTestBase.cs b/test/EFCore.Specification.Tests/Query/Relationships/ComplexProperties/ComplexPropertiesProjectionTestBase.cs new file mode 100644 index 00000000000..015cb8b715b --- /dev/null +++ b/test/EFCore.Specification.Tests/Query/Relationships/ComplexProperties/ComplexPropertiesProjectionTestBase.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexProperties; + +public abstract class ComplexPropertiesProjectionTestBase(TFixture fixture) + : RelationshipsProjectionTestBase(fixture) + where TFixture : ComplexPropertiesFixtureBase, new() +{ + // TODO: the following is temporary until change tracking is implemented for complex JSON types (#35962) + private readonly TrackingRewriter _trackingRewriter = new(QueryTrackingBehavior.NoTracking); + + protected override Expression RewriteServerQueryExpression(Expression serverQueryExpression) + { + var rewritten = _trackingRewriter.Visit(serverQueryExpression); + + return rewritten; + } +} diff --git a/test/EFCore.Specification.Tests/Query/Relationships/ComplexProperties/TrackingRewriter.cs b/test/EFCore.Specification.Tests/Query/Relationships/ComplexProperties/TrackingRewriter.cs new file mode 100644 index 00000000000..68e21fb6c57 --- /dev/null +++ b/test/EFCore.Specification.Tests/Query/Relationships/ComplexProperties/TrackingRewriter.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexProperties; + +internal class TrackingRewriter(QueryTrackingBehavior queryTrackingBehavior) : ExpressionVisitor +{ + private static readonly MethodInfo AsNoTrackingMethodInfo + = typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethod(nameof(EntityFrameworkQueryableExtensions.AsNoTracking))!; + + private static readonly MethodInfo AsNoTrackingWithIdentityResolutionMethodInfo + = typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethod(nameof(EntityFrameworkQueryableExtensions.AsNoTrackingWithIdentityResolution))!; + + protected override Expression VisitExtension(Expression expression) + => expression is EntityQueryRootExpression root + ? queryTrackingBehavior switch + { + QueryTrackingBehavior.NoTracking + => Expression.Call(AsNoTrackingMethodInfo.MakeGenericMethod(root.ElementType), root), + QueryTrackingBehavior.NoTrackingWithIdentityResolution + => Expression.Call(AsNoTrackingWithIdentityResolutionMethodInfo.MakeGenericMethod(root.ElementType), root), + QueryTrackingBehavior.TrackAll + => base.VisitExtension(expression), + + _ => throw new UnreachableException() + } + : base.VisitExtension(expression); +} diff --git a/test/EFCore.Specification.Tests/Query/Relationships/Navigations/NavigationsCollectionTestBase.cs b/test/EFCore.Specification.Tests/Query/Relationships/Navigations/NavigationsCollectionTestBase.cs new file mode 100644 index 00000000000..953d1cc7477 --- /dev/null +++ b/test/EFCore.Specification.Tests/Query/Relationships/Navigations/NavigationsCollectionTestBase.cs @@ -0,0 +1,9 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; + +public abstract class NavigationsCollectionTestBase(TFixture fixture) : RelationshipsCollectionTestBase(fixture) + where TFixture : NavigationsFixtureBase, new() +{ +} diff --git a/test/EFCore.Specification.Tests/Query/Relationships/Navigations/NavigationsFixtureBase.cs b/test/EFCore.Specification.Tests/Query/Relationships/Navigations/NavigationsFixtureBase.cs index 562da2ea672..607916224ef 100644 --- a/test/EFCore.Specification.Tests/Query/Relationships/Navigations/NavigationsFixtureBase.cs +++ b/test/EFCore.Specification.Tests/Query/Relationships/Navigations/NavigationsFixtureBase.cs @@ -1,179 +1,77 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; - namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; public abstract class NavigationsFixtureBase : RelationshipsQueryFixtureBase { protected override string StoreName => "NavigationsQueryTest"; - protected override Task SeedAsync(RelationshipsContext context) - { - var rootEntities = RelationshipsData.CreateRootEntities(); - var trunkEntities = RelationshipsData.CreateTrunkEntities(); - var branchEntities = RelationshipsData.CreateBranchEntities(); - var leafEntities = RelationshipsData.CreateLeafEntities(); - - RelationshipsData.WireUp(rootEntities, trunkEntities, branchEntities, leafEntities, wireUpRootToTrunkOnly: false); - - context.Set().AddRange(rootEntities); - context.Set().AddRange(trunkEntities); - context.Set().AddRange(branchEntities); - context.Set().AddRange(leafEntities); - - return context.SaveChangesAsync(); - } + public override bool AreCollectionsOrdered => false; protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) { base.OnModelCreating(modelBuilder, context); - modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); - modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); - modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); - - modelBuilder.Entity() - .HasOne(x => x.OptionalReferenceTrunk) - .WithOne(x => x.OptionalReferenceInverseRoot) - .HasForeignKey(x => x.OptionalReferenceTrunkId) - .IsRequired(false); - - modelBuilder.Entity() - .HasOne(x => x.RequiredReferenceTrunk) - .WithOne(x => x.RequiredReferenceInverseRoot) - .HasForeignKey(x => x.RequiredReferenceTrunkId) - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(true); - - modelBuilder.Entity() - .HasMany(x => x.CollectionTrunk) - .WithOne(x => x.CollectionInverseRoot) - .HasForeignKey(x => x.CollectionRootId) - .OnDelete(DeleteBehavior.Restrict); - - modelBuilder.Entity() - .HasOne(x => x.OptionalReferenceBranch) - .WithOne(x => x.OptionalReferenceInverseTrunk) - .HasForeignKey(x => x.OptionalReferenceBranchId) - .IsRequired(false); - - modelBuilder.Entity() - .HasOne(x => x.RequiredReferenceBranch) - .WithOne(x => x.RequiredReferenceInverseTrunk) - .HasForeignKey(x => x.RequiredReferenceBranchId) - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(true); - - modelBuilder.Entity() - .HasMany(x => x.CollectionBranch) - .WithOne(x => x.CollectionInverseTrunk) - .HasForeignKey(x => x.CollectionTrunkId) - .OnDelete(DeleteBehavior.Restrict); - - modelBuilder.Entity() - .HasOne(x => x.OptionalReferenceLeaf) - .WithOne(x => x.OptionalReferenceInverseBranch) - .HasForeignKey(x => x.OptionalReferenceLeafId) - .IsRequired(false); - - modelBuilder.Entity() - .HasOne(x => x.RequiredReferenceLeaf) - .WithOne(x => x.RequiredReferenceInverseBranch) - .HasForeignKey(x => x.RequiredReferenceLeafId) - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(true); - - modelBuilder.Entity() - .HasMany(x => x.CollectionLeaf) - .WithOne(x => x.CollectionInverseBranch) - .HasForeignKey(x => x.CollectionBranchId) - .OnDelete(DeleteBehavior.Restrict); - } + // Don't use database value generation since e.g. Cosmos doesn't support it. + modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); + modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); - public override IReadOnlyDictionary EntitySorters { get; } = new Dictionary> - { - { typeof(RelationshipsRoot), e => ((RelationshipsRoot?)e)?.Id }, - { typeof(RelationshipsTrunk), e => ((RelationshipsTrunk?)e)?.Id }, - { typeof(RelationshipsBranch), e => ((RelationshipsBranch?)e)?.Id }, - { typeof(RelationshipsLeaf), e => ((RelationshipsLeaf?)e)?.Id } - }.ToDictionary(e => e.Key, e => (object)e.Value); - - public override IReadOnlyDictionary EntityAsserters { get; } = new Dictionary> - { + modelBuilder.Entity(b => { - typeof(RelationshipsRoot), (e, a) => - { - Assert.Equal(e == null, a == null); - - if (a != null) - { - var ee = (RelationshipsRoot)e!; - var aa = (RelationshipsRoot)a; - - Assert.Equal(ee.Id, aa.Id); - - Assert.Equal(ee.Name, aa.Name); - Assert.Equal(ee.RequiredReferenceTrunkId, aa.RequiredReferenceTrunkId); - Assert.Equal(ee.OptionalReferenceTrunkId, aa.OptionalReferenceTrunkId); - } - } - }, + b.HasOne(r => r.RequiredRelated) + .WithOne(r => r.RequiredRelatedInverse) + .HasForeignKey(r => r.RequiredRelatedId) + .IsRequired(true) + .OnDelete(DeleteBehavior.NoAction); // TODO: Move to SQL Server + + b.HasOne(r => r.OptionalRelated) + .WithOne(r => r.OptionalRelatedInverse) + .HasForeignKey(r => r.OptionalRelatedId) + .IsRequired(false) + .OnDelete(DeleteBehavior.NoAction); // TODO: Move to SQL Server + + b.HasMany(r => r.RelatedCollection) + .WithOne(r => r.RelatedCollectionInverse) + .HasForeignKey(r => r.CollectionRootId); + }); + + modelBuilder.Entity(b => { - typeof(RelationshipsTrunk), (e, a) => - { - Assert.Equal(e == null, a == null); - - if (a != null) - { - var ee = (RelationshipsTrunk)e!; - var aa = (RelationshipsTrunk)a; - - Assert.Equal(ee.Id, aa.Id); + b.HasOne(r => r.RequiredNested) + .WithOne(r => r.RequiredNestedInverse) + .HasForeignKey(r => r.RequiredNestedId) + .IsRequired(true) + .OnDelete(DeleteBehavior.NoAction); // TODO: Move to SQL Server + + b.HasOne(e => e.OptionalNested) + .WithOne(r => r.OptionalNestedInverse) + .HasForeignKey(r => r.OptionalNestedId) + .IsRequired(false) + .OnDelete(DeleteBehavior.NoAction); // TODO: Move to SQL Server + + b.HasMany(r => r.NestedCollection) + .WithOne(r => r.NestedCollectionInverse) + .HasForeignKey(r => r.CollectionRelatedId); + }); + } - Assert.Equal(ee.Name, aa.Name); - Assert.Equal(ee.RequiredReferenceBranchId, aa.RequiredReferenceBranchId); - Assert.Equal(ee.OptionalReferenceBranchId, aa.OptionalReferenceBranchId); - Assert.Equal(ee.CollectionRootId, aa.CollectionRootId); - } - } - }, + // With navigations, related entities aren't loaded by default (Include is required), so we override the asserters to + // ignore unloaded navigations. + protected override void NullSafeAssert(object? e, object? a, Action assertAction) + { + if (e is T ee && a is T aa) { - typeof(RelationshipsBranch), (e, a) => - { - Assert.Equal(e == null, a == null); - - if (a != null) - { - var ee = (RelationshipsBranch)e!; - var aa = (RelationshipsBranch)a; - - Assert.Equal(ee.Id, aa.Id); + assertAction(ee, aa); + return; + } - Assert.Equal(ee.Name, aa.Name); - Assert.Equal(ee.RequiredReferenceLeafId, aa.RequiredReferenceLeafId); - Assert.Equal(ee.OptionalReferenceLeafId, aa.OptionalReferenceLeafId); - Assert.Equal(ee.CollectionTrunkId, aa.CollectionTrunkId); - } - } - }, + // Ignore unloaded actual + if (a is null) { - typeof(RelationshipsLeaf), (e, a) => - { - Assert.Equal(e == null, a == null); + return; + } - if (a != null) - { - var ee = (RelationshipsLeaf)e!; - var aa = (RelationshipsLeaf)a; - - Assert.Equal(ee.Id, aa.Id); - - Assert.Equal(ee.Name, aa.Name); - Assert.Equal(ee.CollectionBranchId, aa.CollectionBranchId); - } - } - }, - }.ToDictionary(e => e.Key, e => (object)e.Value); + Assert.Equal(e, a); + } } diff --git a/test/EFCore.Specification.Tests/Query/Relationships/Navigations/NavigationsIncludeTestBase.cs b/test/EFCore.Specification.Tests/Query/Relationships/Navigations/NavigationsIncludeTestBase.cs index 1c2bba60bbf..7b0206016d5 100644 --- a/test/EFCore.Specification.Tests/Query/Relationships/Navigations/NavigationsIncludeTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/Relationships/Navigations/NavigationsIncludeTestBase.cs @@ -1,114 +1,109 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; - namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; public abstract class NavigationsIncludeTestBase(TFixture fixture) : QueryTestBase(fixture) where TFixture : NavigationsFixtureBase, new() { - protected RelationshipsContext CreateContext() - => Fixture.CreateContext(); - [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual Task Include_trunk_required(bool async) + public virtual Task Include_required(bool async) => AssertQuery( async, - ss => ss.Set().Include(x => x.RequiredReferenceTrunk), - elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(x => x.RequiredReferenceTrunk))); + ss => ss.Set().Include(x => x.RequiredRelated), + elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(x => x.RequiredRelated))); [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual Task Include_trunk_optional(bool async) + public virtual Task Include_optional(bool async) => AssertQuery( async, - ss => ss.Set().Include(x => x.OptionalReferenceTrunk), - elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(x => x.OptionalReferenceTrunk!))); + ss => ss.Set().Include(x => x.OptionalRelated), + elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(x => x.OptionalRelated!))); [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual Task Include_trunk_collection(bool async) + public virtual Task Include_collection(bool async) => AssertQuery( async, - ss => ss.Set().Include(x => x.CollectionTrunk), - elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(x => x.CollectionTrunk))); + ss => ss.Set().Include(x => x.RelatedCollection), + elementAsserter: (e, a) => AssertInclude(e, a, new ExpectedInclude(x => x.RelatedCollection))); [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual Task Include_trunk_required_optional_and_collection(bool async) + public virtual Task Include_required_optional_and_collection(bool async) => AssertQuery( async, - ss => ss.Set() - .Include(x => x.RequiredReferenceTrunk) - .Include(x => x.OptionalReferenceTrunk) - .Include(x => x.CollectionTrunk), + ss => ss.Set() + .Include(x => x.RequiredRelated) + .Include(x => x.OptionalRelated) + .Include(x => x.RelatedCollection), assertOrder: true, elementAsserter: (e, a) => AssertInclude( e, a, - new ExpectedInclude(x => x.RequiredReferenceTrunk), - new ExpectedInclude(x => x.OptionalReferenceTrunk!), - new ExpectedInclude(x => x.CollectionTrunk))); + new ExpectedInclude(x => x.RequiredRelated), + new ExpectedInclude(x => x.OptionalRelated!), + new ExpectedInclude(x => x.RelatedCollection))); [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual Task Include_branch_required_required(bool async) + public virtual Task Include_nested(bool async) => AssertQuery( async, - ss => ss.Set().Include(x => x.RequiredReferenceTrunk.RequiredReferenceBranch), + ss => ss.Set().Include(x => x.RequiredRelated.RequiredNested), elementAsserter: (e, a) => AssertInclude( e, a, - new ExpectedInclude(x => x.RequiredReferenceTrunk), - new ExpectedInclude(x => x.RequiredReferenceBranch, "RequiredReferenceTrunk"))); + new ExpectedInclude(x => x.RequiredRelated), + new ExpectedInclude(x => x.RequiredNested, "RequiredRelated"))); [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual Task Include_branch_required_collection(bool async) + public virtual Task Include_nested_optional(bool async) => AssertQuery( async, - ss => ss.Set().Include(x => x.RequiredReferenceTrunk.CollectionBranch), + ss => ss.Set().Include(x => x.OptionalRelated!.OptionalNested), elementAsserter: (e, a) => AssertInclude( e, a, - new ExpectedInclude(x => x.RequiredReferenceTrunk), - new ExpectedInclude(x => x.CollectionBranch, "RequiredReferenceTrunk"))); + new ExpectedInclude(x => x.OptionalRelated!), + new ExpectedInclude(x => x.OptionalNested!, "OptionalRelated"))); [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual Task Include_branch_optional_optional(bool async) + public virtual Task Include_nested_collection(bool async) => AssertQuery( async, - ss => ss.Set().Include(x => x.OptionalReferenceTrunk!.OptionalReferenceBranch), + ss => ss.Set().Include(x => x.RequiredRelated.NestedCollection), elementAsserter: (e, a) => AssertInclude( e, a, - new ExpectedInclude(x => x.OptionalReferenceTrunk!), - new ExpectedInclude(x => x.OptionalReferenceBranch!, "OptionalReferenceTrunk"))); + new ExpectedInclude(x => x.RequiredRelated), + new ExpectedInclude(x => x.NestedCollection, "RequiredRelated"))); [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual Task Include_branch_optional_collection(bool async) + public virtual Task Include_nested_collection_on_optional(bool async) => AssertQuery( async, - ss => ss.Set().Include(x => x.OptionalReferenceTrunk!.CollectionBranch), + ss => ss.Set().Include(x => x.OptionalRelated!.NestedCollection), elementAsserter: (e, a) => AssertInclude( e, a, - new ExpectedInclude(x => x.OptionalReferenceTrunk!), - new ExpectedInclude(x => x.CollectionBranch, "OptionalReferenceTrunk"))); + new ExpectedInclude(x => x.OptionalRelated!), + new ExpectedInclude(x => x.NestedCollection, "OptionalRelated"))); [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual Task Include_branch_collection_collection(bool async) + public virtual Task Include_nested_collection_on_collection(bool async) => AssertQuery( async, - ss => ss.Set().Include(x => x.CollectionTrunk).ThenInclude(x => x.CollectionBranch), + ss => ss.Set().Include(x => x.RelatedCollection).ThenInclude(x => x.NestedCollection), elementAsserter: (e, a) => AssertInclude( e, a, - new ExpectedInclude(x => x.CollectionTrunk!), - new ExpectedInclude(x => x.CollectionBranch, "CollectionTrunk"))); + new ExpectedInclude(x => x.RelatedCollection), + new ExpectedInclude(x => x.NestedCollection, "RelatedCollection"))); } diff --git a/test/EFCore.Specification.Tests/Query/Relationships/Navigations/NavigationsMiscellaneousTestBase.cs b/test/EFCore.Specification.Tests/Query/Relationships/Navigations/NavigationsMiscellaneousTestBase.cs new file mode 100644 index 00000000000..25615f24f66 --- /dev/null +++ b/test/EFCore.Specification.Tests/Query/Relationships/Navigations/NavigationsMiscellaneousTestBase.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; + +public abstract class NavigationsMiscellaneousTestBase(TFixture fixture) + : RelationshipsMiscellaneousTestBase(fixture) + where TFixture : NavigationsFixtureBase, new() +{ +} diff --git a/test/EFCore.Specification.Tests/Query/Relationships/Navigations/NavigationsProjectionTestBase.cs b/test/EFCore.Specification.Tests/Query/Relationships/Navigations/NavigationsProjectionTestBase.cs index 8c682f446c6..66ad5d0c5bf 100644 --- a/test/EFCore.Specification.Tests/Query/Relationships/Navigations/NavigationsProjectionTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/Relationships/Navigations/NavigationsProjectionTestBase.cs @@ -1,51 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; - namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; public abstract class NavigationsProjectionTestBase(TFixture fixture) : RelationshipsProjectionTestBase(fixture) where TFixture : NavigationsFixtureBase, new() { - [ConditionalTheory] - [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_everything(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertQuery( - async, - ss => from r in ss.Set() - join t in ss.Set() on r.Id equals t.Id - join b in ss.Set() on t.Id equals b.Id - join l in ss.Set() on b.Id equals l.Id - select new { r, t, b, l }, - elementSorter: e => e.r.Id, - elementAsserter: (e, a) => - { - AssertEqual(e.r, a.r); - AssertEqual(e.t, a.t); - AssertEqual(e.b, a.b); - AssertEqual(e.l, a.l); - }, - queryTrackingBehavior: queryTrackingBehavior); - - [ConditionalTheory] - [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_everything_using_joins(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertQuery( - async, - ss => from r in ss.Set() - join t in ss.Set() on r.Id equals t.Id - join b in ss.Set() on t.Id equals b.Id - join l in ss.Set() on b.Id equals l.Id - select new { r, t, b, l }, - elementSorter: e => e.r.Id, - elementAsserter: (e, a) => - { - AssertEqual(e.r, a.r); - AssertEqual(e.t, a.t); - AssertEqual(e.b, a.b); - AssertEqual(e.l, a.l); - }, - queryTrackingBehavior: queryTrackingBehavior); } diff --git a/test/EFCore.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsCollectionTestBase.cs b/test/EFCore.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsCollectionTestBase.cs new file mode 100644 index 00000000000..fa92f75465b --- /dev/null +++ b/test/EFCore.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsCollectionTestBase.cs @@ -0,0 +1,9 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; + +public abstract class OwnedNavigationsCollectionTestBase(TFixture fixture) : RelationshipsCollectionTestBase(fixture) + where TFixture : OwnedNavigationsFixtureBase, new() +{ +} diff --git a/test/EFCore.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsFixtureBase.cs b/test/EFCore.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsFixtureBase.cs index 2454a8345c4..1adcb7e9c60 100644 --- a/test/EFCore.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsFixtureBase.cs +++ b/test/EFCore.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsFixtureBase.cs @@ -1,18 +1,16 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; - namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; public abstract class OwnedNavigationsFixtureBase : RelationshipsQueryFixtureBase { protected override string StoreName => "OwnedNavigationsQueryTest"; - protected override Task SeedAsync(RelationshipsContext context) + protected override Task SeedAsync(PoolableDbContext context) { - var rootEntitiesWithOwnerships = RelationshipsData.CreateRootEntitiesWithOwnerships(); - context.Set().AddRange(rootEntitiesWithOwnerships); + var rootEntities = RelationshipsData.CreateRootEntities(); + context.Set().AddRange(rootEntities); return context.SaveChangesAsync(); } @@ -21,523 +19,55 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con { base.OnModelCreating(modelBuilder, context); - modelBuilder.Entity() - .OwnsOne(x => x.OptionalReferenceTrunk, b => + modelBuilder.Entity(b => + { + b.OwnsOne(e => e.RequiredRelated, rrb => { - b.Ignore(x => x.Id); - b.Ignore(x => x.OptionalReferenceInverseRoot); - b.Ignore(x => x.OptionalReferenceBranchId); - b.OwnsOne(x => x.OptionalReferenceBranch, bb => - { - bb.Ignore(x => x.Id); - bb.Ignore(x => x.OptionalReferenceInverseTrunk); - bb.Ignore(x => x.OptionalReferenceLeafId); - bb.OwnsOne(x => x.OptionalReferenceLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - bb.Navigation(x => x.OptionalReferenceLeaf).IsRequired(false); - - bb.Ignore(x => x.RequiredReferenceInverseTrunk); - bb.Ignore(x => x.RequiredReferenceLeafId); - bb.OwnsOne(x => x.RequiredReferenceLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - bb.Navigation(x => x.RequiredReferenceLeaf).IsRequired(true); - - bb.Ignore(x => x.CollectionInverseTrunk); - bb.Ignore(x => x.CollectionTrunkId); - bb.OwnsMany(x => x.CollectionLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - }); - b.Navigation(x => x.OptionalReferenceBranch).IsRequired(false); - - b.Ignore(x => x.RequiredReferenceInverseRoot); - b.Ignore(x => x.RequiredReferenceBranchId); - b.OwnsOne(x => x.RequiredReferenceBranch, bb => - { - bb.Ignore(x => x.Id); - bb.Ignore(x => x.OptionalReferenceInverseTrunk); - bb.Ignore(x => x.OptionalReferenceLeafId); - bb.OwnsOne(x => x.OptionalReferenceLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - bb.Navigation(x => x.OptionalReferenceLeaf).IsRequired(false); - - bb.Ignore(x => x.RequiredReferenceInverseTrunk); - bb.Ignore(x => x.RequiredReferenceLeafId); - bb.OwnsOne(x => x.RequiredReferenceLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - bb.Navigation(x => x.RequiredReferenceLeaf).IsRequired(true); - - bb.Ignore(x => x.CollectionInverseTrunk); - bb.Ignore(x => x.CollectionTrunkId); - bb.OwnsMany(x => x.CollectionLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - }); - b.Navigation(x => x.RequiredReferenceBranch).IsRequired(true); + rrb.Property(x => x.Id).ValueGeneratedNever(); - b.Ignore(x => x.CollectionInverseRoot); - b.Ignore(x => x.CollectionRootId); - b.OwnsMany(x => x.CollectionBranch, bb => - { - bb.Ignore(x => x.Id); - bb.Ignore(x => x.OptionalReferenceInverseTrunk); - bb.Ignore(x => x.OptionalReferenceLeafId); - bb.OwnsOne(x => x.OptionalReferenceLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - bb.Navigation(x => x.OptionalReferenceLeaf).IsRequired(false); + rrb.OwnsOne(r => r.RequiredNested, rnb => rnb.Property(x => x.Id).ValueGeneratedNever()); + rrb.Navigation(x => x.RequiredNested).IsRequired(true); - bb.Ignore(x => x.RequiredReferenceInverseTrunk); - bb.Ignore(x => x.RequiredReferenceLeafId); - bb.OwnsOne(x => x.RequiredReferenceLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - bb.Navigation(x => x.RequiredReferenceLeaf).IsRequired(true); + rrb.OwnsOne(r => r.OptionalNested, rnb => rnb.Property(x => x.Id).ValueGeneratedNever()); + rrb.Navigation(x => x.RequiredNested).IsRequired(false); - bb.Ignore(x => x.CollectionInverseTrunk); - bb.Ignore(x => x.CollectionTrunkId); - bb.OwnsMany(x => x.CollectionLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - }); + rrb.OwnsMany(r => r.NestedCollection, rcb => rcb.Property(x => x.Id).ValueGeneratedNever()); }); - modelBuilder.Entity().Navigation(x => x.OptionalReferenceTrunk).IsRequired(false); + b.Navigation(x => x.RequiredRelated).IsRequired(true); - modelBuilder.Entity() - .OwnsOne(x => x.RequiredReferenceTrunk, b => + b.OwnsOne(e => e.OptionalRelated, orb => { - b.Ignore(x => x.Id); - b.Ignore(x => x.OptionalReferenceInverseRoot); - b.Ignore(x => x.OptionalReferenceBranchId); - b.OwnsOne(x => x.OptionalReferenceBranch, bb => - { - bb.Ignore(x => x.Id); - bb.Ignore(x => x.OptionalReferenceInverseTrunk); - bb.Ignore(x => x.OptionalReferenceLeafId); - bb.OwnsOne(x => x.OptionalReferenceLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - bb.Navigation(x => x.OptionalReferenceLeaf).IsRequired(false); + orb.Property(x => x.Id).ValueGeneratedNever(); - bb.Ignore(x => x.RequiredReferenceInverseTrunk); - bb.Ignore(x => x.RequiredReferenceLeafId); - bb.OwnsOne(x => x.RequiredReferenceLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - bb.Navigation(x => x.RequiredReferenceLeaf).IsRequired(true); + orb.OwnsOne(r => r.RequiredNested, rnb => rnb.Property(x => x.Id).ValueGeneratedNever()); + orb.Navigation(x => x.RequiredNested).IsRequired(true); - bb.Ignore(x => x.CollectionInverseTrunk); - bb.Ignore(x => x.CollectionTrunkId); - bb.OwnsMany(x => x.CollectionLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - }); - b.Navigation(x => x.OptionalReferenceBranch).IsRequired(false); + orb.OwnsOne(r => r.OptionalNested, rnb => rnb.Property(x => x.Id).ValueGeneratedNever()); + orb.Navigation(x => x.RequiredNested).IsRequired(false); - b.Ignore(x => x.RequiredReferenceInverseRoot); - b.Ignore(x => x.RequiredReferenceBranchId); - b.OwnsOne(x => x.RequiredReferenceBranch, bb => - { - bb.Ignore(x => x.Id); - bb.Ignore(x => x.OptionalReferenceInverseTrunk); - bb.Ignore(x => x.OptionalReferenceLeafId); - bb.OwnsOne(x => x.OptionalReferenceLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - bb.Navigation(x => x.OptionalReferenceLeaf).IsRequired(false); + orb.Ignore(r => r.NestedCollection); - bb.Ignore(x => x.RequiredReferenceInverseTrunk); - bb.Ignore(x => x.RequiredReferenceLeafId); - bb.OwnsOne(x => x.RequiredReferenceLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - bb.Navigation(x => x.RequiredReferenceLeaf).IsRequired(true); - - bb.Ignore(x => x.CollectionInverseTrunk); - bb.Ignore(x => x.CollectionTrunkId); - bb.OwnsMany(x => x.CollectionLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - }); - b.Navigation(x => x.RequiredReferenceBranch).IsRequired(true); - - b.Ignore(x => x.CollectionInverseRoot); - b.Ignore(x => x.CollectionRootId); - b.OwnsMany(x => x.CollectionBranch, bb => - { - bb.Ignore(x => x.Id); - bb.Ignore(x => x.OptionalReferenceInverseTrunk); - bb.Ignore(x => x.OptionalReferenceLeafId); - bb.OwnsOne(x => x.OptionalReferenceLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - bb.Navigation(x => x.OptionalReferenceLeaf).IsRequired(false); - - bb.Ignore(x => x.RequiredReferenceInverseTrunk); - bb.Ignore(x => x.RequiredReferenceLeafId); - bb.OwnsOne(x => x.RequiredReferenceLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - bb.Navigation(x => x.RequiredReferenceLeaf).IsRequired(true); - - bb.Ignore(x => x.CollectionInverseTrunk); - bb.Ignore(x => x.CollectionTrunkId); - bb.OwnsMany(x => x.CollectionLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - }); + orb.OwnsMany(r => r.NestedCollection, rcb => rcb.Property(x => x.Id).ValueGeneratedNever()); }); - modelBuilder.Entity().Navigation(x => x.RequiredReferenceTrunk).IsRequired(true); + b.Navigation(x => x.OptionalRelated).IsRequired(false); - modelBuilder.Entity() - .OwnsMany(x => x.CollectionTrunk, b => + b.OwnsMany(e => e.RelatedCollection, rcb => { - b.Ignore(x => x.Id); - b.Ignore(x => x.OptionalReferenceInverseRoot); - b.Ignore(x => x.OptionalReferenceBranchId); - b.OwnsOne(x => x.OptionalReferenceBranch, bb => - { - bb.Ignore(x => x.Id); - bb.Ignore(x => x.OptionalReferenceInverseTrunk); - bb.Ignore(x => x.OptionalReferenceLeafId); - bb.OwnsOne(x => x.OptionalReferenceLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - bb.Navigation(x => x.OptionalReferenceLeaf).IsRequired(false); - - bb.Ignore(x => x.RequiredReferenceInverseTrunk); - bb.Ignore(x => x.RequiredReferenceLeafId); - bb.OwnsOne(x => x.RequiredReferenceLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - bb.Navigation(x => x.RequiredReferenceLeaf).IsRequired(true); - - bb.Ignore(x => x.CollectionInverseTrunk); - bb.Ignore(x => x.CollectionTrunkId); - bb.OwnsMany(x => x.CollectionLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - }); - b.Navigation(x => x.OptionalReferenceBranch).IsRequired(false); - - b.Ignore(x => x.RequiredReferenceInverseRoot); - b.Ignore(x => x.RequiredReferenceBranchId); - b.OwnsOne(x => x.RequiredReferenceBranch, bb => - { - bb.Ignore(x => x.Id); - bb.Ignore(x => x.OptionalReferenceInverseTrunk); - bb.Ignore(x => x.OptionalReferenceLeafId); - bb.OwnsOne(x => x.OptionalReferenceLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - bb.Navigation(x => x.OptionalReferenceLeaf).IsRequired(false); + rcb.Property(x => x.Id).ValueGeneratedNever(); - bb.Ignore(x => x.RequiredReferenceInverseTrunk); - bb.Ignore(x => x.RequiredReferenceLeafId); - bb.OwnsOne(x => x.RequiredReferenceLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - bb.Navigation(x => x.RequiredReferenceLeaf).IsRequired(true); + rcb.OwnsOne(r => r.RequiredNested, rnb => rnb.Property(x => x.Id).ValueGeneratedNever()); + rcb.Navigation(x => x.RequiredNested).IsRequired(true); - bb.Ignore(x => x.CollectionInverseTrunk); - bb.Ignore(x => x.CollectionTrunkId); - bb.OwnsMany(x => x.CollectionLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - }); - b.Navigation(x => x.RequiredReferenceBranch).IsRequired(true); + rcb.OwnsOne(r => r.OptionalNested, rnb => rnb.Property(x => x.Id).ValueGeneratedNever()); + rcb.Navigation(x => x.RequiredNested).IsRequired(false); - b.Ignore(x => x.CollectionInverseRoot); - b.Ignore(x => x.CollectionRootId); - b.OwnsMany(x => x.CollectionBranch, bb => - { - bb.Ignore(x => x.Id); - bb.Ignore(x => x.OptionalReferenceInverseTrunk); - bb.Ignore(x => x.OptionalReferenceLeafId); - bb.OwnsOne(x => x.OptionalReferenceLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - bb.Navigation(x => x.OptionalReferenceLeaf).IsRequired(false); - - bb.Ignore(x => x.RequiredReferenceInverseTrunk); - bb.Ignore(x => x.RequiredReferenceLeafId); - bb.OwnsOne(x => x.RequiredReferenceLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - bb.Navigation(x => x.RequiredReferenceLeaf).IsRequired(true); - - bb.Ignore(x => x.CollectionInverseTrunk); - bb.Ignore(x => x.CollectionTrunkId); - bb.OwnsMany(x => x.CollectionLeaf, bbb => - { - bbb.Ignore(x => x.Id); - bbb.Ignore(x => x.OptionalReferenceInverseBranch); - bbb.Ignore(x => x.RequiredReferenceInverseBranch); - bbb.Ignore(x => x.CollectionInverseBranch); - bbb.Ignore(x => x.CollectionBranchId); - }); - }); + rcb.OwnsMany(r => r.NestedCollection, rcb => rcb.Property(x => x.Id).ValueGeneratedNever()); }); + }); } - public override IReadOnlyDictionary EntitySorters { get; } = new Dictionary> - { - { typeof(RelationshipsRoot), e => ((RelationshipsRoot?)e)?.Id }, - { typeof(RelationshipsTrunk), e => ((RelationshipsTrunk?)e)?.Name }, - { typeof(RelationshipsBranch), e => ((RelationshipsBranch?)e)?.Name }, - { typeof(RelationshipsLeaf), e => ((RelationshipsLeaf?)e)?.Name } - }.ToDictionary(e => e.Key, e => (object)e.Value); - - public override IReadOnlyDictionary EntityAsserters { get; } = new Dictionary> - { - { - typeof(RelationshipsRoot), (e, a) => - { - Assert.Equal(e == null, a == null); - - if (a != null) - { - var ee = (RelationshipsRoot)e!; - var aa = (RelationshipsRoot)a; - - Assert.Equal(ee.Name, aa.Name); - AssertOwnedTrunk(ee.RequiredReferenceTrunk, aa.RequiredReferenceTrunk); - Assert.Equal(ee.OptionalReferenceTrunk == null, aa.OptionalReferenceTrunk == null); - - if (ee.OptionalReferenceTrunk != null && aa.OptionalReferenceTrunk != null) - { - AssertOwnedTrunk(ee.OptionalReferenceTrunk, aa.OptionalReferenceTrunk); - } - - // TODO: cosmos may return null for empty collections, consider having separate asserters for cosmos - // once models are established (to avoid multiple copy-paste while we iterate over it initially) - Assert.Equal(ee.CollectionTrunk.Count, aa.CollectionTrunk?.Count ?? 0); - for (var i = 0; i < ee.CollectionTrunk.Count; i++) - { - AssertOwnedTrunk(ee.CollectionTrunk[i], aa.CollectionTrunk![i]); - } - } - } - }, - { - typeof(RelationshipsTrunk), (e, a) => - { - Assert.Equal(e == null, a == null); - - if (a != null) - { - var ee = (RelationshipsTrunk)e!; - var aa = (RelationshipsTrunk)a; - AssertOwnedTrunk(ee, aa); - } - } - }, - { - typeof(RelationshipsBranch), (e, a) => - { - Assert.Equal(e == null, a == null); - - if (a != null) - { - var ee = (RelationshipsBranch)e!; - var aa = (RelationshipsBranch)a; - AssertOwnedBranch(ee, aa); - } - } - }, - { - typeof(RelationshipsLeaf), (e, a) => - { - Assert.Equal(e == null, a == null); - - if (a != null) - { - var ee = (RelationshipsLeaf)e!; - var aa = (RelationshipsLeaf)a; - AssertOwnedLeaf(ee, aa); - } - } - }, - }.ToDictionary(e => e.Key, e => (object)e.Value); - - public static void AssertOwnedTrunk(RelationshipsTrunk expected, RelationshipsTrunk actual) - { - Assert.Equal(expected.Name, actual.Name); - - AssertOwnedBranch(expected.RequiredReferenceBranch, actual.RequiredReferenceBranch); - - Assert.Equal(expected.OptionalReferenceBranch == null, actual.OptionalReferenceBranch == null); - if (expected.OptionalReferenceBranch != null && actual.OptionalReferenceBranch != null) - { - AssertOwnedBranch(expected.OptionalReferenceBranch, actual.OptionalReferenceBranch); - } - - Assert.Equal(expected.CollectionBranch.Count, actual.CollectionBranch?.Count ?? 0); - var expectedCollection = expected.CollectionBranch.OrderBy(e => e.Name).ToList(); - var actualCollection = actual.CollectionBranch is null ? [] : actual.CollectionBranch.OrderBy(e => e.Name).ToList(); - for (var i = 0; i < expected.CollectionBranch.Count; i++) - { - AssertOwnedBranch(expectedCollection[i], actualCollection![i]); - } - } - - public static void AssertOwnedBranch(RelationshipsBranch expected, RelationshipsBranch actual) - { - Assert.Equal(expected.Name, actual.Name); - - AssertOwnedLeaf(expected.RequiredReferenceLeaf, actual.RequiredReferenceLeaf); - - Assert.Equal(expected.OptionalReferenceLeaf == null, actual.OptionalReferenceLeaf == null); - if (expected.OptionalReferenceLeaf != null && actual.OptionalReferenceLeaf != null) - { - AssertOwnedLeaf(expected.OptionalReferenceLeaf, actual.OptionalReferenceLeaf); - } - - Assert.Equal(expected.CollectionLeaf.Count, actual.CollectionLeaf?.Count ?? 0); - var expectedCollection = expected.CollectionLeaf.OrderBy(e => e.Name).ToList(); - var actualCollection = actual.CollectionLeaf is null ? [] : actual.CollectionLeaf.OrderBy(e => e.Name).ToList(); - for (var i = 0; i < expected.CollectionLeaf.Count; i++) - { - AssertOwnedLeaf(expectedCollection[i], actualCollection![i]); - } - } - - public static void AssertOwnedLeaf(RelationshipsLeaf expected, RelationshipsLeaf actual) - { - Assert.Equal(expected.Name, actual.Name); - } + // Derived fixtures may need to ignore some owned navigations that are mapped in this fixture. + public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) + => builder.ConfigureWarnings(b => + b.Default(WarningBehavior.Ignore).Log(CoreEventId.MappedNavigationIgnoredWarning)); } diff --git a/test/EFCore.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsMiscellaneousTestBase.cs b/test/EFCore.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsMiscellaneousTestBase.cs new file mode 100644 index 00000000000..2b0fd402a7f --- /dev/null +++ b/test/EFCore.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsMiscellaneousTestBase.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; + +public abstract class OwnedNavigationsMiscellaneousTestBase(TFixture fixture) + : RelationshipsMiscellaneousTestBase(fixture) + where TFixture : OwnedNavigationsFixtureBase, new() +{ +} diff --git a/test/EFCore.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionTestBase.cs b/test/EFCore.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionTestBase.cs index b8375a46196..18a63ab30e2 100644 --- a/test/EFCore.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionTestBase.cs @@ -3,92 +3,236 @@ namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; -public abstract class OwnedNavigationsProjectionTestBase(TFixture fixture) - : RelationshipsProjectionTestBase(fixture) - where TFixture : OwnedNavigationsFixtureBase, new() +public abstract class OwnedNavigationsProjectionTestBase(TFixture fixture) : RelationshipsProjectionTestBase(fixture) + where TFixture : OwnedNavigationsFixtureBase, new() { - public override Task Select_root(bool async, QueryTrackingBehavior queryTrackingBehavior) - => base.Select_root(async, queryTrackingBehavior); + #region Non-collection - public override Task Select_trunk_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.Select_trunk_optional(async, queryTrackingBehavior)); + public override Task Select_related(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertOwnedTrackingQuery(queryTrackingBehavior, () => base.Select_related(async, queryTrackingBehavior)); - public override Task Select_trunk_required(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.Select_trunk_required(async, queryTrackingBehavior)); + public override Task Select_optional_related(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertOwnedTrackingQuery(queryTrackingBehavior, () => base.Select_optional_related(async, queryTrackingBehavior)); - public override Task Select_trunk_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.Select_trunk_collection(async, queryTrackingBehavior)); + public override Task Select_required_related_required_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertOwnedTrackingQuery(queryTrackingBehavior, () => base.Select_required_related_required_nested(async, queryTrackingBehavior)); - public override Task Select_branch_required_required(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.Select_branch_required_required(async, queryTrackingBehavior)); + public override Task Select_required_related_optional_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertOwnedTrackingQuery(queryTrackingBehavior, () => base.Select_required_related_optional_nested(async, queryTrackingBehavior)); - public override Task Select_branch_required_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.Select_branch_required_optional(async, queryTrackingBehavior)); + public override Task Select_optional_related_required_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertOwnedTrackingQuery(queryTrackingBehavior, () => base.Select_optional_related_required_nested(async, queryTrackingBehavior)); - public override Task Select_branch_optional_required(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.Select_branch_optional_required(async, queryTrackingBehavior)); + public override Task Select_optional_related_optional_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertOwnedTrackingQuery(queryTrackingBehavior, () => base.Select_optional_related_optional_nested(async, queryTrackingBehavior)); - public override Task Select_branch_optional_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.Select_branch_optional_optional(async, queryTrackingBehavior)); + #endregion Non-collection - public override Task Select_branch_required_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.Select_branch_required_collection(async, queryTrackingBehavior)); + #region Collection - public override Task Select_branch_optional_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.Select_branch_optional_collection(async, queryTrackingBehavior)); + public override Task Select_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertOwnedTrackingQuery(queryTrackingBehavior, () => base.Select_related_collection(async, queryTrackingBehavior)); - #region Multiple - - public override Task Select_root_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) - => base.Select_root_duplicated(async, queryTrackingBehavior); + public override Task Select_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertOwnedTrackingQuery(queryTrackingBehavior, () => base.Select_required_related_nested_collection(async, queryTrackingBehavior)); - public override Task Select_trunk_and_branch_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.Select_trunk_and_branch_duplicated(async, queryTrackingBehavior)); + public override Task Select_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertOwnedTrackingQuery(queryTrackingBehavior, () => base.Select_optional_related_nested_collection(async, queryTrackingBehavior)); - public override Task Select_trunk_and_trunk_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.Select_trunk_and_trunk_duplicated(async, queryTrackingBehavior)); + public override Task SelectMany_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertOwnedTrackingQuery(queryTrackingBehavior, () => base.SelectMany_related_collection(async, queryTrackingBehavior)); - public override Task Select_leaf_trunk_root(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.Select_leaf_trunk_root(async, queryTrackingBehavior)); + public override Task SelectMany_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertOwnedTrackingQuery(queryTrackingBehavior, () => base.SelectMany_required_related_nested_collection(async, queryTrackingBehavior)); - public override Task Select_multiple_branch_leaf(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.Select_multiple_branch_leaf(async, queryTrackingBehavior)); + public override Task SelectMany_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertOwnedTrackingQuery(queryTrackingBehavior, () => base.SelectMany_optional_related_nested_collection(async, queryTrackingBehavior)); - #endregion Multiple + #endregion Collection - #region Subquery - - public override Task Select_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.Select_subquery_root_set_required_trunk_FirstOrDefault_branch(async, queryTrackingBehavior)); + #region Multiple - public override Task Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(async, queryTrackingBehavior)); + // [ConditionalTheory] + // [MemberData(nameof(AsyncAndTrackingData))] + // public virtual Task Select_trunk_and_branch_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + // => AssertQuery( + // async, + // ss => ss.Set() + // .OrderBy(x => x.Id) + // .Select( + // x => new + // { + // Trunk1 = x.OptionalTrunk, + // Branch1 = x.OptionalTrunk!.RequiredBranch, + // Trunk2 = x.OptionalTrunk, + // Branch2 = x.OptionalTrunk.RequiredBranch, + // }), + // assertOrder: true, + // elementAsserter: (e, a) => + // { + // AssertEqual(e.Trunk1, a.Trunk1); + // AssertEqual(e.Trunk2, a.Trunk2); + // AssertEqual(e.Branch1, a.Branch1); + // AssertEqual(e.Branch2, a.Branch2); + // }, + // queryTrackingBehavior: queryTrackingBehavior); + + // [ConditionalTheory] + // [MemberData(nameof(AsyncAndTrackingData))] + // public virtual Task Select_trunk_and_trunk_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + // => AssertQuery( + // async, + // ss => ss.Set() + // .OrderBy(x => x.Id) + // .Select( + // x => new + // { + // Trunk1 = x.RequiredTrunk, + // Leaf1 = x.RequiredTrunk!.OptionalBranch!.RequiredLeaf, + // Trunk2 = x.RequiredTrunk, + // Leaf2 = x.RequiredTrunk.OptionalBranch.RequiredLeaf, + // }), + // assertOrder: true, + // elementAsserter: (e, a) => + // { + // AssertEqual(e.Trunk1, a.Trunk1); + // AssertEqual(e.Trunk2, a.Trunk2); + // AssertEqual(e.Leaf1, a.Leaf1); + // AssertEqual(e.Leaf2, a.Leaf2); + // }, + // queryTrackingBehavior: queryTrackingBehavior); + + // [ConditionalTheory] + // [MemberData(nameof(AsyncAndTrackingData))] + // public virtual Task Select_leaf_trunk_root(bool async, QueryTrackingBehavior queryTrackingBehavior) + // => AssertQuery( + // async, + // ss => ss.Set() + // .Select( + // x => new + // { + // RequiredReferenceLeaf=x.RequiredTrunk.RequiredBranch.RequiredLeaf, + // RequiredReferenceTrunk= x.RequiredTrunk, + // x + // }), + // elementSorter: e => e.x.Id, + // elementAsserter: (e, a) => + // { + // AssertEqual(e.RequiredReferenceLeaf, a.RequiredReferenceLeaf); + // AssertEqual(e.RequiredReferenceTrunk, a.RequiredReferenceTrunk); + // AssertEqual(e.x, a.x); + // }, + // queryTrackingBehavior: queryTrackingBehavior); + + // [ConditionalTheory] + // [MemberData(nameof(AsyncAndTrackingData))] + // public virtual Task Select_multiple_branch_leaf(bool async, QueryTrackingBehavior queryTrackingBehavior) + // => AssertQuery( + // async, + // ss => ss.Set().Select( + // x => new + // { + // x.Id, + // RequiredReferenceBranch= x.RequiredTrunk.RequiredBranch, + // OptionalReferenceLeaf= x.RequiredTrunk.RequiredBranch.OptionalLeaf, + // CollectionLeaf=x.RequiredTrunk.RequiredBranch.Leaves, + // CollectionBranch= x.RequiredTrunk.Branches, + // x.RequiredTrunk.RequiredBranch.OptionalLeaf!.Name + // }), + // elementSorter: e => e.Id, + // elementAsserter: (e, a) => + // { + // Assert.Equal(e.Id, a.Id); + // AssertEqual(e.RequiredReferenceBranch, a.RequiredReferenceBranch); + // AssertEqual(e.OptionalReferenceLeaf, a.OptionalReferenceLeaf); + // AssertCollection(e.CollectionLeaf, a.CollectionLeaf, ordered: false); + // AssertCollection(e.CollectionBranch, a.CollectionBranch, ordered: false); + // Assert.Equal(e.Name, a.Name); + // }, + // queryTrackingBehavior: queryTrackingBehavior); - public override Task Select_subquery_root_set_trunk_FirstOrDefault_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.Select_subquery_root_set_trunk_FirstOrDefault_collection(async, queryTrackingBehavior)); + #endregion Multiple - public override Task Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(async, queryTrackingBehavior)); + #region Subquery - public override Task Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(async, queryTrackingBehavior)); + public override Task Select_subquery_required_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertOwnedTrackingQuery(queryTrackingBehavior, () => base.Select_subquery_required_related_FirstOrDefault(async, queryTrackingBehavior)); + + public override Task Select_subquery_optional_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertOwnedTrackingQuery(queryTrackingBehavior, () => base.Select_subquery_optional_related_FirstOrDefault(async, queryTrackingBehavior)); + + // [ConditionalTheory] + // [MemberData(nameof(AsyncAndTrackingData))] + // public virtual Task Select_subquery_root_set_trunk_FirstOrDefault_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + // => AssertQuery( + // async, + // ss => ss.Set() + // .OrderBy(x => x.Id) + // .Select( + // x => ss.Set() + // .OrderBy(xx => xx.Id) + // .Select(xx => xx.RequiredTrunk) + // .FirstOrDefault()!.Branches), + // assertOrder: true, + // elementAsserter: (e, a) => AssertCollection(e, a, elementSorter: ee => ee), + // queryTrackingBehavior: queryTrackingBehavior); + + // [ConditionalTheory] + // [MemberData(nameof(AsyncAndTrackingData))] + // public virtual Task Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + // => AssertQuery( + // async, + // ss => ss.Set() + // .OrderBy(x => x.Id) + // .Select( + // x => ss.Set() + // .OrderBy(xx => xx.Id) + // .Select( + // xx => new + // { + // OuterBranch = x.RequiredTrunk.Branches, + // RequiredReferenceTrunk= xx.RequiredTrunk, + // RequiredReferenceBranch=xx.RequiredTrunk.RequiredBranch, + // xx.RequiredTrunk.Name, + // OuterName = x.RequiredTrunk!.RequiredBranch.Name + // }).FirstOrDefault()), + // assertOrder: true, + // elementAsserter: (e, a) => + // { + // AssertCollection(e.OuterBranch, a.OuterBranch); + // AssertEqual(e.RequiredReferenceTrunk, a.RequiredReferenceTrunk); + // AssertEqual(e.RequiredReferenceBranch, a.RequiredReferenceBranch); + // AssertEqual(e.Name, a.Name); + // AssertEqual(e.OuterName, a.OuterName); + // }, + // queryTrackingBehavior: queryTrackingBehavior); + + // [ConditionalTheory] + // [MemberData(nameof(AsyncAndTrackingData))] + // public virtual Task Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(bool async, QueryTrackingBehavior queryTrackingBehavior) + // => AssertQuery( + // async, + // ss => ss.Set() + // .OrderBy(x => x.Id) + // .Select( + // x => ss.Set() + // .OrderBy(xx => xx.Id) + // .Select( + // xx => new + // { + // OuterBranchCollection = x.RequiredTrunk.Branches, + // xx.RequiredTrunk, + // xx.RequiredTrunk.RequiredBranch, + // xx.RequiredTrunk.Name, + // OuterName = x.RequiredTrunk.RequiredBranch.Name + // }).FirstOrDefault()!.OuterBranchCollection), + // assertOrder: true, + // elementAsserter: (e, a) => AssertCollection(e, a), + // queryTrackingBehavior: queryTrackingBehavior); #endregion Subquery - #region SelectMany - - public override Task SelectMany_trunk_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.SelectMany_trunk_collection(async, queryTrackingBehavior)); - - public override Task SelectMany_required_trunk_reference_branch_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.SelectMany_required_trunk_reference_branch_collection(async, queryTrackingBehavior)); - - public override Task SelectMany_optional_trunk_reference_branch_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertCantTrackOwned(queryTrackingBehavior, () => base.SelectMany_optional_trunk_reference_branch_collection(async, queryTrackingBehavior)); - - #endregion SelectMany - - private async Task AssertCantTrackOwned(QueryTrackingBehavior queryTrackingBehavior, Func test) + protected virtual async Task AssertOwnedTrackingQuery(QueryTrackingBehavior queryTrackingBehavior, Func test) { if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) { diff --git a/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsCollectionTestBase.cs b/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsCollectionTestBase.cs new file mode 100644 index 00000000000..e0fa150a24f --- /dev/null +++ b/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsCollectionTestBase.cs @@ -0,0 +1,96 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships; + +/// +/// Contains tests for apply LINQ operators to collection relationships. +/// +public abstract class RelationshipsCollectionTestBase(TFixture fixture) : QueryTestBase(fixture) + where TFixture : RelationshipsQueryFixtureBase, new() +{ + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Count(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(e => e.RelatedCollection.Count == 2)); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(e => e.RelatedCollection.Where(r => r.Int != 50).Count() == 2)); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task OrderBy_ElementAt(bool async) + => AssertQuery( + async, + ss => ss.Set().Where( + e => e.RelatedCollection.OrderBy(r => r.Id).ElementAt(0).Int == 21), + ss => ss.Set().Where(e => e.RelatedCollection.Count > 0 + && e.RelatedCollection.OrderBy(r => r.Id).ElementAt(0).Int == 21)); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Index_constant(bool async) + => AssertOrderedCollectionQuery( + () => AssertQuery( + async, + ss => ss.Set().Where(e => e.RelatedCollection[0].Int == 21), + ss => ss.Set().Where(e => e.RelatedCollection.Count > 0 && e.RelatedCollection[0].Int == 21))); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Index_parameter(bool async) + => AssertOrderedCollectionQuery(() => + { + var i = 0; + + return AssertQuery( + async, + ss => ss.Set().Where(e => e.RelatedCollection[i].Int == 21), + ss => ss.Set().Where(e => e.RelatedCollection.Count > 0 && e.RelatedCollection[i].Int == 21)); + }); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Index_column(bool async) + => AssertOrderedCollectionQuery( + () => AssertQuery( + async, + ss => ss.Set().Where(e => e.RelatedCollection[e.Id - 1].Int == 21), + ss => ss.Set().Where(e => e.RelatedCollection.Count > e.Id - 1 && e.RelatedCollection[e.Id - 1].Int == 21))); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Index_out_of_bounds(bool async) + => AssertOrderedCollectionQuery( + () => AssertQuery( + async, + ss => ss.Set().Where(e => e.RelatedCollection[9999].Int == 50), + ss => ss.Set().Where(e => false), + assertEmpty: true)); + + /// + /// Utility for tests that depend on the collection being naturally ordered + /// (e.g. JSON collection as opposed to a classical relational collection navigation, which is unordered). + /// + private async Task AssertOrderedCollectionQuery(Func action) + { + if (Fixture.AreCollectionsOrdered) + { + await action(); + } + else + { + // An error was generated for warning 'Microsoft.EntityFrameworkCore.Query.RowLimitingOperationWithoutOrderByWarning': + // The query uses a row limiting operator ('Skip'/'Take') without an 'OrderBy' operator. + // This may lead to unpredictable results. If the 'Distinct' operator is used after 'OrderBy', then make sure to use the 'OrderBy' operator after 'Distinct' as the ordering would otherwise get erased. + // This exception can be suppressed or logged by passing event ID 'CoreEventId.RowLimitingOperationWithoutOrderByWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'. + await Assert.ThrowsAsync(action); + } + } +} diff --git a/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsData.cs b/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsData.cs new file mode 100644 index 00000000000..6079ee7b5e4 --- /dev/null +++ b/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsData.cs @@ -0,0 +1,304 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships; + +public class RelationshipsData : ISetSource +{ + public RelationshipsData(bool withCollections = true) + { + RootEntities = CreateRootEntities(withCollections); + MainTypes = []; + NestedTypes = []; + PreRootEntities = []; + } + + // TODO: Remove? Relevant only for non-owned navigations + public List RootEntities { get; } + public List MainTypes { get; } + public List NestedTypes { get; } + public List PreRootEntities { get; } + + public static List CreateRootEntities(bool withCollections = true) + { + List rootEntities = + [ + new RootEntity + { + Id = 1, + Name = "Root1", + + RequiredRelated = new RelatedType + { + Id = 100, + Name = "Root1_RequiredRelated", + + Int = 8, + String = "foo", + + RequiredNested = new NestedType + { + Id = 1000, + Name = "Root1_RequiredRelated_RequiredNested", + + Int = 50, + String = "foo_foo" + }, + OptionalNested = new NestedType + { + Id = 1001, + Name = "Root1_RequiredRelated_OptionalNested", + + Int = 51, + String = "foo_bar" + }, + NestedCollection = + [ + new NestedType + { + Id = 1002, + Name = "Root1_RequiredRelated_NestedCollection_1", + + Int = 52, + String = "foo_baz1" + }, + new NestedType + { + Id = 1003, + Name = "Root1_RequiredRelated_NestedCollection_2", + + Int = 53, + String = "foo_baz2" + } + ] + }, + OptionalRelated = new RelatedType + { + Id = 101, + Name = "Root1_OptionalRelated", + + Int = 9, + String = "bar", + + RequiredNested = new NestedType + { + Id = 1010, + Name = "Root1_OptionalRelated_RequiredNested", + + Int = 52, + String = "bar_foo" + }, + OptionalNested = new NestedType + { + Id = 1011, + Name = "Root1_OptionalRelated_OptionalNested", + + Int = 53, + String = "bar_bar" + }, + NestedCollection = + [ + new NestedType + { + Id = 1012, + Name = "Root1_OptionalRelated_NestedCollection_1", + + Int = 54, + String = "bar_baz1" + }, + new NestedType + { + Id = 1013, + Name = "Root1_OptionalRelated_NestedCollection_2", + + Int = 55, + String = "bar_baz2" + } + ] + }, + RelatedCollection = + [ + new RelatedType + { + Id = 102, + Name = "Root1_RelatedCollection_1", + + Int = 21, + String = "foo", + + RequiredNested = new NestedType + { + Id = 1020, + Name = "Root1_RelatedCollection_1_RequiredNested", + + Int = 50, + String = "foo_foo" + }, + OptionalNested = new NestedType + { + Id = 1021, + Name = "Root1_RelatedCollection_1_OptionalNested", + + Int = 51, + String = "foo_bar" + }, + NestedCollection = + [ + new NestedType + { + Id = 1022, + Name = "Root1_RelatedCollection_1_NestedCollection_1", + + Int = 53, + String = "foo_bar" + }, + new NestedType + { + Id = 1023, + Name = "Root1_RelatedCollection_1_NestedCollection_2", + + Int = 51, + String = "foo_bar" + } + ] + }, + new RelatedType + { + Id = 103, + Name = "Root1_RelatedCollection_2", + + Int = 22, + String = "foo", + + RequiredNested = new NestedType + { + Id = 1030, + Name = "Root1_RelatedCollection_2_RequiredNested", + + Int = 50, + String = "foo_foo" + }, + OptionalNested = new NestedType + { + Id = 1031, + Name = "Root1_RelatedCollection_2_OptionalNested", + + Int = 51, + String = "foo_bar" + }, + NestedCollection = + [ + new NestedType + { + Id = 1032, + Name = "Root1_RelatedCollection_2_NestedCollection_1", + + Int = 53, + String = "foo_bar" + }, + new NestedType + { + Id = 1033, + Name = "Root1_RelatedCollection_2_NestedCollection_2", + + Int = 51, + String = "foo_bar" + } + ] + } + ] + }, + new RootEntity + { + Id = 2, + Name = "Root2", + + RequiredRelated = new RelatedType + { + Id = 200, + Name = "Root2_RequiredRelated", + + Int = 10, + String = "aaa", + + RequiredNested = new NestedType + { + Id = 2000, + Name = "Root2_RequiredRelated_RequiredNested", + + Int = 54, + String = "aaa_xxx" + }, + OptionalNested = new NestedType + { + Id = 2001, + Name = "Root2_RequiredRelated_OptionalNested", + + Int = 55, + String = "aaa_yyy" + }, + NestedCollection = + [ + // TODO + ] + }, + OptionalRelated = new RelatedType + { + Id = 201, + Name = "Root2_OptionalRelated", + + Int = 11, + String = "bbb", + + RequiredNested = new NestedType + { + Id = 2010, + Name = "Root2_OptionalRelated_RequiredNested", + + Int = 56, + String = "bbb_xxx" + }, + OptionalNested = new NestedType + { + Id = 2011, + Name = "Root2_OptionalRelated_OptionalNested", + + Int = 57, + String = "bbb_yyy" + }, + NestedCollection = + [ + // TODO + ] + }, + RelatedCollection = + [ + ] + } + ]; + + if (!withCollections) + { + foreach (var rootEntity in rootEntities) + { + rootEntity.RelatedCollection = []; + rootEntity.RequiredRelated.NestedCollection = []; + rootEntity.OptionalRelated?.NestedCollection = []; + } + } + + return rootEntities; + } + + public IQueryable Set() + where TEntity : class + => typeof(TEntity) switch + { + var t when t == typeof(RootEntity) => (IQueryable)RootEntities.AsQueryable(), + var t when t == typeof(RelatedType) => (IQueryable)MainTypes.AsQueryable(), + var t when t == typeof(NestedType) => (IQueryable)NestedTypes.AsQueryable(), + var t when t == typeof(RootReferencingEntity) => (IQueryable)PreRootEntities.AsQueryable(), + + _ => throw new InvalidOperationException("Invalid entity type: " + typeof(TEntity)) + }; +} diff --git a/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsMiscellaneousTestBase.cs b/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsMiscellaneousTestBase.cs new file mode 100644 index 00000000000..a84e4b6f7d0 --- /dev/null +++ b/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsMiscellaneousTestBase.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships; + +public abstract class RelationshipsMiscellaneousTestBase(TFixture fixture) : QueryTestBase(fixture) + where TFixture : RelationshipsQueryFixtureBase, new() +{ + #region Simple filters + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_related_property(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(e => e.RequiredRelated.Int == 8)); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_optional_related_property(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(e => e.OptionalRelated!.Int == 9)); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Where_nested_related_property(bool async) + => AssertQuery( + async, + ss => ss.Set().Where(e => e.RequiredRelated.RequiredNested.Int == 50)); + + #endregion Simple filters +} diff --git a/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsModel.cs b/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsModel.cs new file mode 100644 index 00000000000..26c448315b8 --- /dev/null +++ b/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsModel.cs @@ -0,0 +1,97 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel.DataAnnotations.Schema; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships; + +/// +/// Main entity type used as the root for most test queries. +/// References , which represents the main type/relationship to be tested. +/// +public class RootEntity +{ + public int Id { get; set; } + public required string Name { get; set; } + + public required RelatedType RequiredRelated { get; set; } + public RelatedType? OptionalRelated { get; set; } + public List RelatedCollection { get; set; } = null!; + + public RootReferencingEntity? RootReferencingEntity { get; set; } + + // Foreign keys and inverse navigations are unmapped by default, and are explicitly mapped via + // the Fluent API for navigation tests only. + [NotMapped] // Explicitly mapped via Fluent API for navigation tests only + public int RequiredRelatedId { get; set; } + [NotMapped] // Explicitly mapped via Fluent API for navigation tests only + public int OptionalRelatedId { get; set; } +} + +/// +/// The main type to be tested; mapped differently (entity type, complex type...) across +/// different test variations. +/// +public class RelatedType +{ + public int Id { get; set; } + public required string Name { get; set; } + + public int Int { get; set; } + public string String { get; set; } = null!; + + public required NestedType RequiredNested { get; set; } + public NestedType? OptionalNested { get; set; } + public List NestedCollection { get; set; } = null!; + + // Foreign keys and inverse navigations are unmapped by default, and are explicitly mapped via + // the Fluent API for navigation tests only. + [NotMapped] + public int RequiredNestedId { get; set; } + [NotMapped] + public int? OptionalNestedId { get; set; } + + [NotMapped] + public RootEntity RequiredRelatedInverse { get; set; } = null!; + [NotMapped] + public RootEntity OptionalRelatedInverse { get; set; } = null!; + [NotMapped] + public RootEntity RelatedCollectionInverse { get; set; } = null!; + [NotMapped] + public int? CollectionRootId { get; set; } +} + +/// +/// An additional nested type contained within , for tests which exercise +/// nested relationships. +/// +public class NestedType +{ + public int Id { get; set; } + public required string Name { get; set; } + + public int Int { get; set; } + public string String { get; set; } = null!; + + // Foreign keys and inverse navigations are unmapped by default, and are explicitly mapped via + // the Fluent API for navigation tests only. + [NotMapped] + public RelatedType RequiredNestedInverse { get; set; } = null!; + [NotMapped] + public RelatedType OptionalNestedInverse { get; set; } = null!; + [NotMapped] + public RelatedType NestedCollectionInverse { get; set; } = null!; + [NotMapped] + public int? CollectionRelatedId { get; set; } +} + +/// +/// Regular entity type referencing RootEntity, which is also a regular entity type. +/// Used to test e.g. projecting complex types on via optional (null) navigations. +/// +public class RootReferencingEntity +{ + public int Id { get; set; } + + public RootEntity? Root { get; set; } = null!; +} diff --git a/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsProjectionTestBase.cs b/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsProjectionTestBase.cs index 39b4984b720..2a381abb3f3 100644 --- a/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsProjectionTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsProjectionTestBase.cs @@ -1,356 +1,376 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; - namespace Microsoft.EntityFrameworkCore.Query.Relationships; public abstract class RelationshipsProjectionTestBase(TFixture fixture) : QueryTestBase(fixture) where TFixture : RelationshipsQueryFixtureBase, new() { - protected RelationshipsContext CreateContext() - => Fixture.CreateContext(); - [ConditionalTheory] [MemberData(nameof(AsyncAndTrackingData))] public virtual Task Select_root(bool async, QueryTrackingBehavior queryTrackingBehavior) => AssertQuery( async, - ss => ss.Set(), + ss => ss.Set(), queryTrackingBehavior: queryTrackingBehavior); + #region Simple properties + [ConditionalTheory] [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_trunk_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) + public virtual Task Select_related_property(bool async, QueryTrackingBehavior queryTrackingBehavior) => AssertQuery( async, - ss => ss.Set().OrderBy(x => x.Id).Select(x => x.OptionalReferenceTrunk), - assertOrder: true, + ss => ss.Set().Select(x => x.RequiredRelated.String), queryTrackingBehavior: queryTrackingBehavior); [ConditionalTheory] [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_trunk_required(bool async, QueryTrackingBehavior queryTrackingBehavior) + public virtual Task Select_optional_related_property(bool async, QueryTrackingBehavior queryTrackingBehavior) => AssertQuery( async, - ss => ss.Set().OrderBy(x => x.Id).Select(x => x.RequiredReferenceTrunk), - assertOrder: true, + ss => ss.Set().Select(x => x.OptionalRelated!.String), queryTrackingBehavior: queryTrackingBehavior); [ConditionalTheory] [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_trunk_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public virtual Task Select_optional_related_property_value_type(bool async, QueryTrackingBehavior queryTrackingBehavior) => AssertQuery( async, - ss => ss.Set().OrderBy(x => x.Id).Select(x => x.CollectionTrunk), - assertOrder: true, - elementAsserter: (e, a) => AssertCollection(e, a, elementSorter: ee => ee.Name), + ss => ss.Set().Select(x => x.OptionalRelated!.Int), queryTrackingBehavior: queryTrackingBehavior); + #endregion Simple properties + + #region Non-collection + [ConditionalTheory] [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_branch_required_required(bool async, QueryTrackingBehavior queryTrackingBehavior) + public virtual Task Select_related(bool async, QueryTrackingBehavior queryTrackingBehavior) => AssertQuery( async, - ss => ss.Set().OrderBy(x => x.Id).Select(x => x.RequiredReferenceTrunk.RequiredReferenceBranch), - assertOrder: true, + ss => ss.Set().Select(x => x.RequiredRelated), queryTrackingBehavior: queryTrackingBehavior); [ConditionalTheory] [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_branch_required_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) + public virtual Task Select_optional_related(bool async, QueryTrackingBehavior queryTrackingBehavior) => AssertQuery( async, - ss => ss.Set().OrderBy(x => x.Id).Select(x => x.RequiredReferenceTrunk.OptionalReferenceBranch), - assertOrder: true, + ss => ss.Set().Select(x => x.OptionalRelated), queryTrackingBehavior: queryTrackingBehavior); [ConditionalTheory] [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_branch_optional_required(bool async, QueryTrackingBehavior queryTrackingBehavior) + public virtual Task Select_required_related_required_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) => AssertQuery( async, - ss => ss.Set().OrderBy(x => x.Id).Select(x => x.RequiredReferenceTrunk.RequiredReferenceBranch), - assertOrder: true, + ss => ss.Set().Select(x => x.RequiredRelated.RequiredNested), queryTrackingBehavior: queryTrackingBehavior); [ConditionalTheory] [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_branch_optional_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) + public virtual Task Select_required_related_optional_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) => AssertQuery( async, - ss => ss.Set().OrderBy(x => x.Id).Select(x => x.RequiredReferenceTrunk.OptionalReferenceBranch), - assertOrder: true, + ss => ss.Set().Select(x => x.RequiredRelated.OptionalNested), queryTrackingBehavior: queryTrackingBehavior); [ConditionalTheory] [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_branch_required_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public virtual Task Select_optional_related_required_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) => AssertQuery( async, - ss => ss.Set().OrderBy(x => x.Id).Select(x => x.RequiredReferenceTrunk.CollectionBranch), - assertOrder: true, - elementAsserter: (e, a) => AssertCollection(e, a, elementSorter: ee => ee.Name), + ss => ss.Set().Select(x => x.OptionalRelated!.RequiredNested), queryTrackingBehavior: queryTrackingBehavior); [ConditionalTheory] [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_branch_optional_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public virtual Task Select_optional_related_optional_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) => AssertQuery( async, - ss => ss.Set().OrderBy(x => x.Id).Select(x => x.RequiredReferenceTrunk.CollectionBranch), - assertOrder: true, - elementAsserter: (e, a) => AssertCollection(e, a, elementSorter: ee => ee.Name), + ss => ss.Set().Select(x => x.OptionalRelated!.OptionalNested), queryTrackingBehavior: queryTrackingBehavior); - #region Multiple + #endregion Non-collection - [ConditionalTheory] - [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_root_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertQuery( - async, - ss => ss.Set().Select(x => new { First = x, Second = x }), - elementSorter: e => e.First.Id, - elementAsserter: (e, a) => - { - AssertEqual(e.First, a.First); - AssertEqual(e.Second, a.Second); - }, - queryTrackingBehavior: queryTrackingBehavior); + #region Collection + + // Note we order via the Id (server-side) to ensure the collections come back in deterministic order, + // otherwise it's difficult/unreliable to compare client-side. [ConditionalTheory] [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_trunk_and_branch_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + public virtual Task Select_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) => AssertQuery( async, - ss => ss.Set() - .OrderBy(x => x.Id) - .Select( - x => new - { - Trunk1 = x.OptionalReferenceTrunk, - Branch1 = x.OptionalReferenceTrunk!.RequiredReferenceBranch, - Trunk2 = x.OptionalReferenceTrunk, - Branch2 = x.OptionalReferenceTrunk.RequiredReferenceBranch, - }), + ss => ss.Set().OrderBy(e => e.Id).Select(x => x.RelatedCollection), assertOrder: true, - elementAsserter: (e, a) => - { - AssertEqual(e.Trunk1, a.Trunk1); - AssertEqual(e.Trunk2, a.Trunk2); - AssertEqual(e.Branch1, a.Branch1); - AssertEqual(e.Branch2, a.Branch2); - }, + elementAsserter: (e, a) => AssertCollection(e, a, elementSorter: r => r.Id), queryTrackingBehavior: queryTrackingBehavior); [ConditionalTheory] [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_trunk_and_trunk_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + public virtual Task Select_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) => AssertQuery( async, - ss => ss.Set() - .OrderBy(x => x.Id) - .Select( - x => new - { - Trunk1 = x.RequiredReferenceTrunk, - Leaf1 = x.RequiredReferenceTrunk!.OptionalReferenceBranch!.RequiredReferenceLeaf, - Trunk2 = x.RequiredReferenceTrunk, - Leaf2 = x.RequiredReferenceTrunk.OptionalReferenceBranch.RequiredReferenceLeaf, - }), + ss => ss.Set().Select(x => x.RequiredRelated.NestedCollection), assertOrder: true, - elementAsserter: (e, a) => - { - AssertEqual(e.Trunk1, a.Trunk1); - AssertEqual(e.Trunk2, a.Trunk2); - AssertEqual(e.Leaf1, a.Leaf1); - AssertEqual(e.Leaf2, a.Leaf2); - }, + elementAsserter: (e, a) => AssertCollection(e, a, elementSorter: r => r.Id), queryTrackingBehavior: queryTrackingBehavior); [ConditionalTheory] [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_leaf_trunk_root(bool async, QueryTrackingBehavior queryTrackingBehavior) + public virtual Task Select_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) => AssertQuery( async, - ss => ss.Set() - .Select( - x => new - { - x.RequiredReferenceTrunk.RequiredReferenceBranch.RequiredReferenceLeaf, - x.RequiredReferenceTrunk, - x - }), - elementSorter: e => e.x.Id, - elementAsserter: (e, a) => - { - AssertEqual(e.RequiredReferenceLeaf, a.RequiredReferenceLeaf); - AssertEqual(e.RequiredReferenceTrunk, a.RequiredReferenceTrunk); - AssertEqual(e.x, a.x); - }, + ss => ss.Set().Select(x => x.OptionalRelated!.NestedCollection), + assertOrder: true, + elementAsserter: (e, a) => AssertCollection(e, a, elementSorter: r => r.Id), queryTrackingBehavior: queryTrackingBehavior); [ConditionalTheory] [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_multiple_branch_leaf(bool async, QueryTrackingBehavior queryTrackingBehavior) + public virtual Task SelectMany_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) => AssertQuery( async, - ss => ss.Set().Select( - x => new - { - x.Id, - x.RequiredReferenceTrunk.RequiredReferenceBranch, - x.RequiredReferenceTrunk.RequiredReferenceBranch.OptionalReferenceLeaf, - x.RequiredReferenceTrunk.RequiredReferenceBranch.CollectionLeaf, - x.RequiredReferenceTrunk.CollectionBranch, - x.RequiredReferenceTrunk.RequiredReferenceBranch.OptionalReferenceLeaf!.Name - }), - elementSorter: e => e.Id, - elementAsserter: (e, a) => - { - Assert.Equal(e.Id, a.Id); - AssertEqual(e.RequiredReferenceBranch, a.RequiredReferenceBranch); - AssertEqual(e.OptionalReferenceLeaf, a.OptionalReferenceLeaf); - AssertCollection(e.CollectionLeaf, a.CollectionLeaf, ordered: false); - AssertCollection(e.CollectionBranch, a.CollectionBranch, ordered: false); - Assert.Equal(e.Name, a.Name); - }, + ss => ss.Set().SelectMany(x => x.RelatedCollection), queryTrackingBehavior: queryTrackingBehavior); - #endregion Multiple - - #region Subquery - [ConditionalTheory] [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) + public virtual Task SelectMany_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) => AssertQuery( async, - ss => ss.Set() - .OrderBy(x => x.Id) - .Select( - x => ss.Set() - .OrderBy(xx => xx.Id) - .Select(xx => xx.RequiredReferenceTrunk) - .FirstOrDefault()!.RequiredReferenceBranch), - assertOrder: true, + ss => ss.Set().SelectMany(x => x.RequiredRelated.NestedCollection), queryTrackingBehavior: queryTrackingBehavior); [ConditionalTheory] [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) + public virtual Task SelectMany_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) => AssertQuery( async, - ss => ss.Set() - .OrderBy(x => x.Id) - .Select( - x => ss.Set() - .OrderBy(xx => xx.Id) - .Select(xx => xx.OptionalReferenceTrunk) - .FirstOrDefault()!.OptionalReferenceBranch), - assertOrder: true, + ss => ss.Set() + .SelectMany(x => x.OptionalRelated!.NestedCollection), + ss => ss.Set() + .SelectMany(x => x.OptionalRelated.Maybe(xx => xx!.NestedCollection) ?? new List()), queryTrackingBehavior: queryTrackingBehavior); - [ConditionalTheory] - [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_subquery_root_set_trunk_FirstOrDefault_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertQuery( - async, - ss => ss.Set() - .OrderBy(x => x.Id) - .Select( - x => ss.Set() - .OrderBy(xx => xx.Id) - .Select(xx => xx.RequiredReferenceTrunk) - .FirstOrDefault()!.CollectionBranch), - assertOrder: true, - elementAsserter: (e, a) => AssertCollection(e, a, elementSorter: ee => ee), - queryTrackingBehavior: queryTrackingBehavior); + #endregion Collection + + #region Multiple [ConditionalTheory] [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + public virtual Task Select_root_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) => AssertQuery( async, - ss => ss.Set() - .OrderBy(x => x.Id) - .Select( - x => ss.Set() - .OrderBy(xx => xx.Id) - .Select( - xx => new - { - OuterBranch = x.RequiredReferenceTrunk.CollectionBranch, - xx.RequiredReferenceTrunk, - xx.RequiredReferenceTrunk.RequiredReferenceBranch, - xx.RequiredReferenceTrunk.Name, - OuterName = x.RequiredReferenceTrunk!.RequiredReferenceBranch.Name - }).FirstOrDefault()), - assertOrder: true, + ss => ss.Set().Select(x => new { First = x, Second = x }), + elementSorter: e => e.First.Id, elementAsserter: (e, a) => { - AssertCollection(e.OuterBranch, a.OuterBranch); - AssertEqual(e.RequiredReferenceTrunk, a.RequiredReferenceTrunk); - AssertEqual(e.RequiredReferenceBranch, a.RequiredReferenceBranch); - AssertEqual(e.Name, a.Name); - AssertEqual(e.OuterName, a.OuterName); + AssertEqual(e.First, a.First); + AssertEqual(e.Second, a.Second); }, queryTrackingBehavior: queryTrackingBehavior); - [ConditionalTheory] - [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertQuery( - async, - ss => ss.Set() - .OrderBy(x => x.Id) - .Select( - x => ss.Set() - .OrderBy(xx => xx.Id) - .Select( - xx => new - { - OuterBranchCollection = x.RequiredReferenceTrunk.CollectionBranch, - xx.RequiredReferenceTrunk, - xx.RequiredReferenceTrunk.RequiredReferenceBranch, - xx.RequiredReferenceTrunk.Name, - OuterName = x.RequiredReferenceTrunk.RequiredReferenceBranch.Name - }).FirstOrDefault()!.OuterBranchCollection), - assertOrder: true, - elementAsserter: (e, a) => AssertCollection(e, a), - queryTrackingBehavior: queryTrackingBehavior); + // [ConditionalTheory] + // [MemberData(nameof(AsyncAndTrackingData))] + // public virtual Task Select_trunk_and_branch_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + // => AssertQuery( + // async, + // ss => ss.Set() + // .OrderBy(x => x.Id) + // .Select( + // x => new + // { + // Trunk1 = x.OptionalTrunk, + // Branch1 = x.OptionalTrunk!.RequiredBranch, + // Trunk2 = x.OptionalTrunk, + // Branch2 = x.OptionalTrunk.RequiredBranch, + // }), + // assertOrder: true, + // elementAsserter: (e, a) => + // { + // AssertEqual(e.Trunk1, a.Trunk1); + // AssertEqual(e.Trunk2, a.Trunk2); + // AssertEqual(e.Branch1, a.Branch1); + // AssertEqual(e.Branch2, a.Branch2); + // }, + // queryTrackingBehavior: queryTrackingBehavior); + + // [ConditionalTheory] + // [MemberData(nameof(AsyncAndTrackingData))] + // public virtual Task Select_trunk_and_trunk_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + // => AssertQuery( + // async, + // ss => ss.Set() + // .OrderBy(x => x.Id) + // .Select( + // x => new + // { + // Trunk1 = x.RequiredTrunk, + // Leaf1 = x.RequiredTrunk!.OptionalBranch!.RequiredLeaf, + // Trunk2 = x.RequiredTrunk, + // Leaf2 = x.RequiredTrunk.OptionalBranch.RequiredLeaf, + // }), + // assertOrder: true, + // elementAsserter: (e, a) => + // { + // AssertEqual(e.Trunk1, a.Trunk1); + // AssertEqual(e.Trunk2, a.Trunk2); + // AssertEqual(e.Leaf1, a.Leaf1); + // AssertEqual(e.Leaf2, a.Leaf2); + // }, + // queryTrackingBehavior: queryTrackingBehavior); + + // [ConditionalTheory] + // [MemberData(nameof(AsyncAndTrackingData))] + // public virtual Task Select_leaf_trunk_root(bool async, QueryTrackingBehavior queryTrackingBehavior) + // => AssertQuery( + // async, + // ss => ss.Set() + // .Select( + // x => new + // { + // RequiredReferenceLeaf=x.RequiredTrunk.RequiredBranch.RequiredLeaf, + // RequiredReferenceTrunk= x.RequiredTrunk, + // x + // }), + // elementSorter: e => e.x.Id, + // elementAsserter: (e, a) => + // { + // AssertEqual(e.RequiredReferenceLeaf, a.RequiredReferenceLeaf); + // AssertEqual(e.RequiredReferenceTrunk, a.RequiredReferenceTrunk); + // AssertEqual(e.x, a.x); + // }, + // queryTrackingBehavior: queryTrackingBehavior); + + // [ConditionalTheory] + // [MemberData(nameof(AsyncAndTrackingData))] + // public virtual Task Select_multiple_branch_leaf(bool async, QueryTrackingBehavior queryTrackingBehavior) + // => AssertQuery( + // async, + // ss => ss.Set().Select( + // x => new + // { + // x.Id, + // RequiredReferenceBranch= x.RequiredTrunk.RequiredBranch, + // OptionalReferenceLeaf= x.RequiredTrunk.RequiredBranch.OptionalLeaf, + // CollectionLeaf=x.RequiredTrunk.RequiredBranch.Leaves, + // CollectionBranch= x.RequiredTrunk.Branches, + // x.RequiredTrunk.RequiredBranch.OptionalLeaf!.Name + // }), + // elementSorter: e => e.Id, + // elementAsserter: (e, a) => + // { + // Assert.Equal(e.Id, a.Id); + // AssertEqual(e.RequiredReferenceBranch, a.RequiredReferenceBranch); + // AssertEqual(e.OptionalReferenceLeaf, a.OptionalReferenceLeaf); + // AssertCollection(e.CollectionLeaf, a.CollectionLeaf, ordered: false); + // AssertCollection(e.CollectionBranch, a.CollectionBranch, ordered: false); + // Assert.Equal(e.Name, a.Name); + // }, + // queryTrackingBehavior: queryTrackingBehavior); - #endregion Subquery + #endregion Multiple - #region SelectMany + #region Subquery [ConditionalTheory] [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task SelectMany_trunk_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public virtual Task Select_subquery_required_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) => AssertQuery( async, - ss => ss.Set().SelectMany(x => x.CollectionTrunk), + ss => ss.Set() + .Select( + x => ss.Set() + .OrderBy(e => e.Id) + .Select(e => e.RequiredRelated) + .FirstOrDefault()!.RequiredNested), queryTrackingBehavior: queryTrackingBehavior); [ConditionalTheory] [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task SelectMany_required_trunk_reference_branch_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public virtual Task Select_subquery_optional_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) => AssertQuery( async, - ss => ss.Set().SelectMany(x => x.RequiredReferenceTrunk.CollectionBranch), + ss => ss.Set() + .Select( + x => ss.Set() + .OrderBy(e => e.Id) + .Select(e => e.OptionalRelated) + .FirstOrDefault()!.RequiredNested), queryTrackingBehavior: queryTrackingBehavior); - [ConditionalTheory] - [MemberData(nameof(AsyncAndTrackingData))] - public virtual Task SelectMany_optional_trunk_reference_branch_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - => AssertQuery( - async, - ss => ss.Set() - .SelectMany(x => x.OptionalReferenceTrunk!.CollectionBranch), - ss => ss.Set() - .SelectMany(x => x.OptionalReferenceTrunk.Maybe(xx => xx!.CollectionBranch) ?? new List()), - queryTrackingBehavior: queryTrackingBehavior); + // [ConditionalTheory] + // [MemberData(nameof(AsyncAndTrackingData))] + // public virtual Task Select_subquery_root_set_trunk_FirstOrDefault_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + // => AssertQuery( + // async, + // ss => ss.Set() + // .OrderBy(x => x.Id) + // .Select( + // x => ss.Set() + // .OrderBy(xx => xx.Id) + // .Select(xx => xx.RequiredTrunk) + // .FirstOrDefault()!.Branches), + // assertOrder: true, + // elementAsserter: (e, a) => AssertCollection(e, a, elementSorter: ee => ee), + // queryTrackingBehavior: queryTrackingBehavior); + + // [ConditionalTheory] + // [MemberData(nameof(AsyncAndTrackingData))] + // public virtual Task Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + // => AssertQuery( + // async, + // ss => ss.Set() + // .OrderBy(x => x.Id) + // .Select( + // x => ss.Set() + // .OrderBy(xx => xx.Id) + // .Select( + // xx => new + // { + // OuterBranch = x.RequiredTrunk.Branches, + // RequiredReferenceTrunk= xx.RequiredTrunk, + // RequiredReferenceBranch=xx.RequiredTrunk.RequiredBranch, + // xx.RequiredTrunk.Name, + // OuterName = x.RequiredTrunk!.RequiredBranch.Name + // }).FirstOrDefault()), + // assertOrder: true, + // elementAsserter: (e, a) => + // { + // AssertCollection(e.OuterBranch, a.OuterBranch); + // AssertEqual(e.RequiredReferenceTrunk, a.RequiredReferenceTrunk); + // AssertEqual(e.RequiredReferenceBranch, a.RequiredReferenceBranch); + // AssertEqual(e.Name, a.Name); + // AssertEqual(e.OuterName, a.OuterName); + // }, + // queryTrackingBehavior: queryTrackingBehavior); + + // [ConditionalTheory] + // [MemberData(nameof(AsyncAndTrackingData))] + // public virtual Task Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(bool async, QueryTrackingBehavior queryTrackingBehavior) + // => AssertQuery( + // async, + // ss => ss.Set() + // .OrderBy(x => x.Id) + // .Select( + // x => ss.Set() + // .OrderBy(xx => xx.Id) + // .Select( + // xx => new + // { + // OuterBranchCollection = x.RequiredTrunk.Branches, + // xx.RequiredTrunk, + // xx.RequiredTrunk.RequiredBranch, + // xx.RequiredTrunk.Name, + // OuterName = x.RequiredTrunk.RequiredBranch.Name + // }).FirstOrDefault()!.OuterBranchCollection), + // assertOrder: true, + // elementAsserter: (e, a) => AssertCollection(e, a), + // queryTrackingBehavior: queryTrackingBehavior); - #endregion SelectMany + #endregion Subquery } diff --git a/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsQueryFixtureBase.cs b/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsQueryFixtureBase.cs index ea74786ca4c..ad850c79138 100644 --- a/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsQueryFixtureBase.cs +++ b/test/EFCore.Specification.Tests/Query/Relationships/RelationshipsQueryFixtureBase.cs @@ -1,31 +1,115 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; - namespace Microsoft.EntityFrameworkCore.Query.Relationships; -public abstract class RelationshipsQueryFixtureBase : SharedStoreFixtureBase, IQueryFixtureBase +public abstract class RelationshipsQueryFixtureBase : SharedStoreFixtureBase, IQueryFixtureBase { - private RelationshipsData? _expectedData; + public virtual bool AreCollectionsOrdered => true; + + private readonly RelationshipsData _data; + + public RelationshipsQueryFixtureBase() + { + _data = CreateData(); + + EntityAsserters = new Dictionary> + { + [typeof(RootEntity)] = (e, a) => NullSafeAssert(e, a, AssertRootEntity), + [typeof(RelatedType)] = (e, a) => NullSafeAssert(e, a, AssertRelatedType), + [typeof(NestedType)] = (e, a) => NullSafeAssert(e, a, AssertNestedType), + [typeof(RootReferencingEntity)] = (e, a) => NullSafeAssert(e, a, AssertPreRootEntity) + }.ToDictionary(e => e.Key, e => (object)e.Value); + } public Func GetContextCreator() - => () => CreateContext(); + => CreateContext; public virtual ISetSource GetExpectedData() - => _expectedData ??= new RelationshipsData(); + => _data; + + protected virtual RelationshipsData CreateData() + => new(); + + protected override Task SeedAsync(PoolableDbContext context) + { + context.Set().AddRange(_data.RootEntities); - protected override Task SeedAsync(RelationshipsContext context) + return context.SaveChangesAsync(); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) { - throw new InvalidOperationException("Override this method in dervided fixtures."); + // Don't use database value generation since e.g. Cosmos doesn't support it. + modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); + modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); + + modelBuilder.Entity() + .HasOne(r => r.Root) + .WithOne(r => r.RootReferencingEntity) + .HasForeignKey("RootEntityId") // TODO: possibly make a CLR property + .IsRequired(false); } - public abstract IReadOnlyDictionary EntitySorters { get; } + public virtual IReadOnlyDictionary EntitySorters { get; } = new Dictionary> + { + { typeof(RootEntity), e => ((RootEntity?)e)?.Id }, + { typeof(RelatedType), e => ((RelatedType?)e)?.Id }, + { typeof(NestedType), e => ((NestedType?)e)?.Id }, + { typeof(RootReferencingEntity), e => ((RootReferencingEntity?)e)?.Id } + }.ToDictionary(e => e.Key, e => (object)e.Value); - public abstract IReadOnlyDictionary EntityAsserters { get; } + public virtual IReadOnlyDictionary EntityAsserters { get; } - protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) + private void AssertRootEntity(RootEntity e, RootEntity a) { - modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); + Assert.Equal(e.Id, a.Id); + Assert.Equal(e.Name, a.Name); + + NullSafeAssert(e.RequiredRelated, a.RequiredRelated, AssertRelatedType); + NullSafeAssert(e.OptionalRelated, a.OptionalRelated, AssertRelatedType); + + // TODO: Complete for collection, mind ordering (how is this done elsewhere?) + } + + private void AssertRelatedType(RelatedType e, RelatedType a) + { + Assert.Equal(e.Id, a.Id); + Assert.Equal(e.Name, a.Name); + + Assert.Equal(e.Int, a.Int); + Assert.Equal(e.String, a.String); + + NullSafeAssert(e.RequiredNested, a.RequiredNested, AssertNestedType); + NullSafeAssert(e.OptionalNested, a.OptionalNested, AssertNestedType); + + // TODO: Complete for collection, mind ordering (how is this done elsewhere?) + } + + private void AssertNestedType(NestedType e, NestedType a) + { + Assert.Equal(e.Id, a.Id); + Assert.Equal(e.Name, a.Name); + + Assert.Equal(e.Int, a.Int); + Assert.Equal(e.String, a.String); + } + + private void AssertPreRootEntity(RootReferencingEntity e, RootReferencingEntity a) + { + Assert.Equal(e.Id, a.Id); + + NullSafeAssert(e.Root, a.Root, AssertRootEntity); + } + + protected virtual void NullSafeAssert(object? e, object? a, Action assertAction) + { + if (e is T ee && a is T aa) + { + assertAction(ee, aa); + return; + } + + Assert.Equal(e, a); } } diff --git a/test/EFCore.Specification.Tests/TestModels/RelationshipsModel/RelationshipsBranchEntity.cs b/test/EFCore.Specification.Tests/TestModels/RelationshipsModel/RelationshipsBranchEntity.cs deleted file mode 100644 index 7770bd27167..00000000000 --- a/test/EFCore.Specification.Tests/TestModels/RelationshipsModel/RelationshipsBranchEntity.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; - -public class RelationshipsBranch -{ - public int Id { get; set; } - - public string Name { get; set; } = null!; - - public int? OptionalReferenceLeafId { get; set; } - public RelationshipsLeaf? OptionalReferenceLeaf { get; set; } = null!; - - public int RequiredReferenceLeafId { get; set; } - public RelationshipsLeaf RequiredReferenceLeaf { get; set; } = null!; - public List CollectionLeaf { get; set; } = null!; - - public RelationshipsTrunk? OptionalReferenceInverseTrunk { get; set; } = null!; - - public RelationshipsTrunk RequiredReferenceInverseTrunk { get; set; } = null!; - - public int? CollectionTrunkId { get; set; } - public RelationshipsTrunk CollectionInverseTrunk { get; set; } = null!; -} diff --git a/test/EFCore.Specification.Tests/TestModels/RelationshipsModel/RelationshipsContext.cs b/test/EFCore.Specification.Tests/TestModels/RelationshipsModel/RelationshipsContext.cs deleted file mode 100644 index a22479b6a20..00000000000 --- a/test/EFCore.Specification.Tests/TestModels/RelationshipsModel/RelationshipsContext.cs +++ /dev/null @@ -1,8 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; - -public class RelationshipsContext(DbContextOptions options) : DbContext(options) -{ -} diff --git a/test/EFCore.Specification.Tests/TestModels/RelationshipsModel/RelationshipsData.cs b/test/EFCore.Specification.Tests/TestModels/RelationshipsModel/RelationshipsData.cs deleted file mode 100644 index a4cb017ef7e..00000000000 --- a/test/EFCore.Specification.Tests/TestModels/RelationshipsModel/RelationshipsData.cs +++ /dev/null @@ -1,312 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; - -public class RelationshipsData : ISetSource -{ - public RelationshipsData() - { - RootEntities = CreateRootEntities(); - TrunkEntities = CreateTrunkEntities(); - BranchEntities = CreateBranchEntities(); - LeafEntities = CreateLeafEntities(); - - WireUp(RootEntities, TrunkEntities, BranchEntities, LeafEntities, wireUpRootToTrunkOnly: false); - } - - public IReadOnlyList RootEntities { get; } - public IReadOnlyList TrunkEntities { get; } - public IReadOnlyList BranchEntities { get; } - public IReadOnlyList LeafEntities { get; } - - public static IReadOnlyList CreateRootEntitiesWithOwnerships() - { - var roots = new List(); - for (var i = 0; i < 16; i++) - { - roots.Add(BuildRootEntity(i)); - } - - return roots; - } - - public static IReadOnlyList CreateTrunkEntitiesWithOwnerships() - { - var trunks = new List(); - for (var i = 0; i < 16; i++) - { - trunks.Add(BuildTrunkEntity(i)); - } - - return trunks; - } - - private static RelationshipsRoot BuildRootEntity(int i) - { - var root = new RelationshipsRoot { Id = i + 1, Name = "Root " + (i + 1) }; - root.RequiredReferenceTrunk = BuildTrunkEntity(15 - i); - if (i < 8) - { - root.OptionalReferenceTrunk = BuildTrunkEntity(i); - } - - root.CollectionTrunk = new List(); - if (i % 4 == 0) - { - root.CollectionTrunk.Add(BuildTrunkEntity(i + 1)); - root.CollectionTrunk.Add(BuildTrunkEntity(i + 2)); - if (i == 0) - { - root.CollectionTrunk.Add(BuildTrunkEntity(4)); - } - } - - return root; - } - - private static RelationshipsTrunk BuildTrunkEntity(int i) - { - var trunk = new RelationshipsTrunk { Id = i + 1, Name = "Trunk " + (i + 1) }; - trunk.RequiredReferenceBranch = BuildBranchEntity(15 - i); - - if (i % 8 < 4) - { - trunk.OptionalReferenceBranch = BuildBranchEntity(i); - } - - trunk.CollectionBranch = new List(); - if (i % 4 == 0) - { - trunk.CollectionBranch.Add(BuildBranchEntity(i + 1)); - trunk.CollectionBranch.Add(BuildBranchEntity(i + 2)); - if (i == 4) - { - trunk.CollectionBranch.Add(BuildBranchEntity(8)); - } - } - - return trunk; - } - - private static RelationshipsBranch BuildBranchEntity(int i) - { - var branch = new RelationshipsBranch { Id = i + 1, Name = "Branch " + (i + 1) }; - branch.RequiredReferenceLeaf = BuildLeafEntity(15 - i); - - if (i % 4 < 2) - { - branch.OptionalReferenceLeaf = BuildLeafEntity(i); - } - - branch.CollectionLeaf = new List(); - if (i % 4 == 0) - { - branch.CollectionLeaf.Add(BuildLeafEntity(i + 1)); - branch.CollectionLeaf.Add(BuildLeafEntity(i + 2)); - if (i == 8) - { - branch.CollectionLeaf.Add(BuildLeafEntity(12)); - } - } - - return branch; - } - - private static RelationshipsLeaf BuildLeafEntity(int i) - => new RelationshipsLeaf { Id = i + 1, Name = "Leaf " + (i + 1) }; - - public static IReadOnlyList CreateRootEntities() - { - var roots = new List(); - - var id = 0; - for (var i = 0; i < 16; i++) - { - roots.Add(new RelationshipsRoot { Id = ++id, Name = "Root " + id }); - } - - return roots; - } - - public static IReadOnlyList CreateTrunkEntities() - { - var trunks = new List(); - - var id = 0; - for (var i = 0; i < 16; i++) - { - trunks.Add(new RelationshipsTrunk { Id = ++id, Name = "Trunk " + id }); - } - - return trunks; - } - - public static IReadOnlyList CreateBranchEntities() - { - var branches = new List(); - - var id = 0; - for (var i = 0; i < 16; i++) - { - branches.Add(new RelationshipsBranch { Id = ++id, Name = "Branch " + id }); - } - - return branches; - } - - public static IReadOnlyList CreateLeafEntities() - { - var leaves = new List(); - - var id = 0; - for (var i = 0; i < 16; i++) - { - leaves.Add(new RelationshipsLeaf { Id = ++id, Name = "Leaf " + id }); - } - - return leaves; - } - - - public static void WireUp( - IReadOnlyList rootEntities, - IReadOnlyList trunkEntities, - IReadOnlyList branchEntities, - IReadOnlyList leafEntities, - bool wireUpRootToTrunkOnly) - { - for (int i = 0; i < 16; i++) - { - rootEntities[i].RequiredReferenceTrunk = trunkEntities[15 - i]; - rootEntities[i].RequiredReferenceTrunkId = trunkEntities[15 - i].Id; - trunkEntities[15 - i].RequiredReferenceInverseRoot = rootEntities[i]; - - if (!wireUpRootToTrunkOnly) - { - trunkEntities[i].RequiredReferenceBranch = branchEntities[15 - i]; - trunkEntities[i].RequiredReferenceBranchId = branchEntities[15 - i].Id; - branchEntities[15 - i].RequiredReferenceInverseTrunk = trunkEntities[i]; - - branchEntities[i].RequiredReferenceLeaf = leafEntities[15 - i]; - branchEntities[i].RequiredReferenceLeafId = leafEntities[15 - i].Id; - leafEntities[15 - i].RequiredReferenceInverseBranch = branchEntities[i]; - } - - rootEntities[i].CollectionTrunk = new List(); - - if (i < 8) - { - rootEntities[i].OptionalReferenceTrunk = trunkEntities[i]; - rootEntities[i].OptionalReferenceTrunkId = trunkEntities[i].Id; - trunkEntities[i].OptionalReferenceInverseRoot = rootEntities[i]; - } - - if (!wireUpRootToTrunkOnly) - { - if (i % 8 < 4) - { - trunkEntities[i].OptionalReferenceBranch = branchEntities[i]; - trunkEntities[i].OptionalReferenceBranchId = branchEntities[i].Id; - branchEntities[i].OptionalReferenceInverseTrunk = trunkEntities[i]; - } - - if (i % 4 < 2) - { - branchEntities[i].OptionalReferenceLeaf = leafEntities[i]; - branchEntities[i].OptionalReferenceLeafId = leafEntities[i].Id; - leafEntities[i].OptionalReferenceInverseBranch = branchEntities[i]; - } - - trunkEntities[i].CollectionBranch = new List(); - branchEntities[i].CollectionLeaf = new List(); - } - - if (i % 4 == 0) - { - rootEntities[i].CollectionTrunk.AddRange(trunkEntities[i + 1], trunkEntities[i + 2]); - if (i == 0) - { - rootEntities[i].CollectionTrunk.Add(trunkEntities[4]); - } - - trunkEntities[i + 1].CollectionInverseRoot = rootEntities[i]; - trunkEntities[i + 1].CollectionRootId = rootEntities[i].Id; - - trunkEntities[i + 2].CollectionInverseRoot = rootEntities[i]; - trunkEntities[i + 2].CollectionRootId = rootEntities[i].Id; - - if (i == 0) - { - trunkEntities[4].CollectionInverseRoot = rootEntities[i]; - trunkEntities[4].CollectionRootId = rootEntities[i].Id; - } - - if (!wireUpRootToTrunkOnly) - { - trunkEntities[i].CollectionBranch.AddRange(branchEntities[i + 1], branchEntities[i + 2]); - if (i == 4) - { - trunkEntities[i].CollectionBranch.Add(branchEntities[8]); - } - - branchEntities[i + 1].CollectionInverseTrunk = trunkEntities[i]; - branchEntities[i + 1].CollectionTrunkId = trunkEntities[i].Id; - - branchEntities[i + 2].CollectionInverseTrunk = trunkEntities[i]; - branchEntities[i + 2].CollectionTrunkId = trunkEntities[i].Id; - - if (i == 4) - { - branchEntities[8].CollectionInverseTrunk = trunkEntities[i]; - branchEntities[8].CollectionTrunkId = trunkEntities[i].Id; - } - - branchEntities[i].CollectionLeaf.AddRange(leafEntities[i + 1], leafEntities[i + 2]); - if (i == 8) - { - branchEntities[i].CollectionLeaf.Add(leafEntities[12]); - } - - leafEntities[i + 1].CollectionInverseBranch = branchEntities[i]; - leafEntities[i + 1].CollectionBranchId = branchEntities[i].Id; - - leafEntities[i + 2].CollectionInverseBranch = branchEntities[i]; - leafEntities[i + 2].CollectionBranchId = branchEntities[i].Id; - - if (i == 8) - { - leafEntities[12].CollectionInverseBranch = branchEntities[i]; - leafEntities[12].CollectionBranchId = branchEntities[i].Id; - } - } - } - } - } - - public IQueryable Set() - where TEntity : class - { - if (typeof(TEntity) == typeof(RelationshipsRoot)) - { - return (IQueryable)RootEntities.AsQueryable(); - } - - if (typeof(TEntity) == typeof(RelationshipsTrunk)) - { - return (IQueryable)TrunkEntities.AsQueryable(); - } - - if (typeof(TEntity) == typeof(RelationshipsBranch)) - { - return (IQueryable)BranchEntities.AsQueryable(); - } - - if (typeof(TEntity) == typeof(RelationshipsLeaf)) - { - return (IQueryable)LeafEntities.AsQueryable(); - } - - throw new InvalidOperationException("Invalid entity type: " + typeof(TEntity)); - } -} diff --git a/test/EFCore.Specification.Tests/TestModels/RelationshipsModel/RelationshipsLeafEntity.cs b/test/EFCore.Specification.Tests/TestModels/RelationshipsModel/RelationshipsLeafEntity.cs deleted file mode 100644 index 6458a3e6c2f..00000000000 --- a/test/EFCore.Specification.Tests/TestModels/RelationshipsModel/RelationshipsLeafEntity.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; - -public class RelationshipsLeaf -{ - public int Id { get; set; } - - public string Name { get; set; } = null!; - - public RelationshipsBranch OptionalReferenceInverseBranch { get; set; } = null!; - - public RelationshipsBranch RequiredReferenceInverseBranch { get; set; } = null!; - - public int? CollectionBranchId { get; set; } - public RelationshipsBranch CollectionInverseBranch { get; set; } = null!; -} diff --git a/test/EFCore.Specification.Tests/TestModels/RelationshipsModel/RelationshipsRootEntity.cs b/test/EFCore.Specification.Tests/TestModels/RelationshipsModel/RelationshipsRootEntity.cs deleted file mode 100644 index 136a4de5533..00000000000 --- a/test/EFCore.Specification.Tests/TestModels/RelationshipsModel/RelationshipsRootEntity.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; - -public class RelationshipsRoot -{ - public int Id { get; set; } - - public string Name { get; set; } = null!; - - public int? OptionalReferenceTrunkId { get; set; } - public RelationshipsTrunk? OptionalReferenceTrunk { get; set; } = null!; - - public int RequiredReferenceTrunkId { get; set; } - public RelationshipsTrunk RequiredReferenceTrunk { get; set; } = null!; - public List CollectionTrunk { get; set; } = null!; -} diff --git a/test/EFCore.Specification.Tests/TestModels/RelationshipsModel/RelationshipsTrunkEntity.cs b/test/EFCore.Specification.Tests/TestModels/RelationshipsModel/RelationshipsTrunkEntity.cs deleted file mode 100644 index f95e504e17d..00000000000 --- a/test/EFCore.Specification.Tests/TestModels/RelationshipsModel/RelationshipsTrunkEntity.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; - -public class RelationshipsTrunk -{ - public int Id { get; set; } - - public string Name { get; set; } = null!; - - public int? OptionalReferenceBranchId { get; set; } - public RelationshipsBranch? OptionalReferenceBranch { get; set; } = null!; - - public int RequiredReferenceBranchId { get; set; } - public RelationshipsBranch RequiredReferenceBranch { get; set; } = null!; - - public List CollectionBranch { get; set; } = null!; - - public RelationshipsRoot? OptionalReferenceInverseRoot { get; set; } = null!; - - public RelationshipsRoot RequiredReferenceInverseRoot { get; set; } = null!; - - public int? CollectionRootId { get; set; } - public RelationshipsRoot CollectionInverseRoot { get; set; } = null!; -} diff --git a/test/EFCore.Specification.Tests/TestUtilities/QueryAsserter.cs b/test/EFCore.Specification.Tests/TestUtilities/QueryAsserter.cs index c290b5bd690..4bc165d58ad 100644 --- a/test/EFCore.Specification.Tests/TestUtilities/QueryAsserter.cs +++ b/test/EFCore.Specification.Tests/TestUtilities/QueryAsserter.cs @@ -1634,8 +1634,8 @@ public void AssertCollection( return; case (null, not null): case (not null, null): - throw new InvalidOperationException( - $"Nullability doesn't match. Expected: {(expected == null ? "NULL" : "NOT NULL")}. Actual: {(actual == null ? "NULL." : "NOT NULL.")}."); + Assert.Fail($"Nullability doesn't match. Expected: {(expected == null ? "NULL" : "NOT NULL")}. Actual: {(actual == null ? "NULL." : "NOT NULL.")}."); + throw new UnreachableException(); case (not null, not null): break; } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs index 353348efa4d..776c0446f1e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs @@ -17,29 +17,29 @@ public ComplexTypeQuerySqlServerTest(ComplexTypeQuerySqlServerFixture fixture, I Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } - public override async Task Filter_on_property_inside_complex_type(bool async) - { - await base.Filter_on_property_inside_complex_type(async); - - AssertSql( - """ -SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName] -FROM [Customer] AS [c] -WHERE [c].[ShippingAddress_ZipCode] = 7728 -"""); - } - - public override async Task Filter_on_property_inside_nested_complex_type(bool async) - { - await base.Filter_on_property_inside_nested_complex_type(async); - - AssertSql( - """ -SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName] -FROM [Customer] AS [c] -WHERE [c].[ShippingAddress_Country_Code] = N'DE' -"""); - } +// public override async Task Filter_on_property_inside_complex_type(bool async) +// { +// await base.Filter_on_property_inside_complex_type(async); + +// AssertSql( +// """ +// SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName] +// FROM [Customer] AS [c] +// WHERE [c].[ShippingAddress_ZipCode] = 7728 +// """); +// } + +// public override async Task Filter_on_property_inside_nested_complex_type(bool async) +// { +// await base.Filter_on_property_inside_nested_complex_type(async); + +// AssertSql( +// """ +// SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName] +// FROM [Customer] AS [c] +// WHERE [c].[ShippingAddress_Country_Code] = N'DE' +// """); +// } public override async Task Filter_on_property_inside_complex_type_after_subquery(bool async) { diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonCollectionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonCollectionSqlServerTest.cs new file mode 100644 index 00000000000..b2107e5a853 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonCollectionSqlServerTest.cs @@ -0,0 +1,118 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit.Sdk; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexJson; + +public class ComplexJsonCollectionSqlServerTest(ComplexJsonSqlServerFixture fixture, ITestOutputHelper testOutputHelper) + : ComplexJsonCollectionRelationalTestBase(fixture, testOutputHelper) +{ + public override async Task Count(bool async) + { + await base.Count(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE ( + SELECT COUNT(*) + FROM OPENJSON([r].[RelatedCollection], '$') AS [r0]) = 2 +"""); + } + + public override async Task Where(bool async) + { + await base.Where(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE ( + SELECT COUNT(*) + FROM OPENJSON([r].[RelatedCollection], '$') WITH ([Int] int '$.Int') AS [r0] + WHERE [r0].[Int] <> 50) = 2 +"""); + } + + public override async Task OrderBy_ElementAt(bool async) + { + await base.OrderBy_ElementAt(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE ( + SELECT [r0].[Int] + FROM OPENJSON([r].[RelatedCollection], '$') WITH ( + [Id] int '$.Id', + [Int] int '$.Int' + ) AS [r0] + ORDER BY [r0].[Id] + OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) = 21 +"""); + } + + public override async Task Index_constant(bool async) + { + // Complex collection indexing currently fails because SubqueryMemberPushdownExpressionVisitor moves the Int member access to before the + // ElementAt (making a Select()), this interferes with our translation. See #36335. + await Assert.ThrowsAsync(() => base.Index_constant(async)); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE CAST(JSON_VALUE([r].[RelatedCollection], '$[0]') AS int) = 21 +"""); + } + + public override async Task Index_parameter(bool async) + { + // Complex collection indexing currently fails because SubqueryMemberPushdownExpressionVisitor moves the Int member access to before the + // ElementAt (making a Select()), this interferes with our translation. See #36335. + await Assert.ThrowsAsync(() => base.Index_parameter(async)); + + AssertSql( + """ +@i='?' (DbType = Int32) + +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE CAST(JSON_VALUE([r].[RelatedCollection], '$[' + CAST(@i AS nvarchar(max)) + ']') AS int) = 21 +"""); + } + + public override async Task Index_column(bool async) + { + // Complex collection indexing currently fails because SubqueryMemberPushdownExpressionVisitor moves the Int member access to before the + // ElementAt (making a Select()), this interferes with our translation. See #36335. + await Assert.ThrowsAsync(() => base.Index_column(async)); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE CAST(JSON_VALUE([r].[RelatedCollection], '$[' + CAST([r].[Id] - 1 AS nvarchar(max)) + ']') AS int) = 21 +"""); + } + + public override async Task Index_out_of_bounds(bool async) + { + await base.Index_out_of_bounds(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE CAST(JSON_VALUE([r].[RelatedCollection], '$[9999]') AS int) = 50 +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonMiscellaneousSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonMiscellaneousSqlServerTest.cs new file mode 100644 index 00000000000..f88248fae34 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonMiscellaneousSqlServerTest.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexJson; + +public class ComplexJsonMiscellaneousSqlServerTest(ComplexJsonSqlServerFixture fixture, ITestOutputHelper testOutputHelper) + : ComplexJsonMiscellaneousRelationalTestBase(fixture, testOutputHelper) +{ + #region Simple filters + + public override async Task Where_related_property(bool async) + { + await base.Where_related_property(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE CAST(JSON_VALUE([r].[RequiredRelated], '$.Int') AS int) = 8 +"""); + } + + public override async Task Where_optional_related_property(bool async) + { + await base.Where_optional_related_property(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE CAST(JSON_VALUE([r].[OptionalRelated], '$.Int') AS int) = 9 +"""); + } + + public override async Task Where_nested_related_property(bool async) + { + await base.Where_nested_related_property(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE CAST(JSON_VALUE([r].[RequiredRelated], '$.RequiredNested.Int') AS int) = 50 +"""); + } + + #endregion Simple filters + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonProjectionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonProjectionSqlServerTest.cs new file mode 100644 index 00000000000..6ad2f6fd5b1 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonProjectionSqlServerTest.cs @@ -0,0 +1,273 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexJson; + +public class ComplexJsonProjectionSqlServerTest(ComplexJsonSqlServerFixture fixture, ITestOutputHelper testOutputHelper) + : ComplexJsonProjectionRelationalTestBase(fixture, testOutputHelper) +{ + public override async Task Select_root(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_root(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +"""); + } + + #region Simple properties + + public override async Task Select_related_property(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_related_property(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT JSON_VALUE([r].[RequiredRelated], '$.String') +FROM [RootEntity] AS [r] +"""); + } + + public override async Task Select_optional_related_property(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_optional_related_property(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT JSON_VALUE([r].[OptionalRelated], '$.String') +FROM [RootEntity] AS [r] +"""); + } + + public override async Task Select_optional_related_property_value_type(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_optional_related_property_value_type(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT CAST(JSON_VALUE([r].[OptionalRelated], '$.Int') AS int) +FROM [RootEntity] AS [r] +"""); + } + + #endregion Simple properties + + #region Non-collection + + public override async Task Select_related(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_related(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT [r].[RequiredRelated] +FROM [RootEntity] AS [r] +"""); + } + + public override async Task Select_optional_related(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_optional_related(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT [r].[OptionalRelated] +FROM [RootEntity] AS [r] +"""); + } + + public override async Task Select_required_related_required_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_required_related_required_nested(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT JSON_QUERY([r].[RequiredRelated], '$.RequiredNested') +FROM [RootEntity] AS [r] +"""); + } + + public override async Task Select_required_related_optional_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_required_related_optional_nested(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT JSON_QUERY([r].[RequiredRelated], '$.OptionalNested') +FROM [RootEntity] AS [r] +"""); + } + + public override async Task Select_optional_related_required_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_optional_related_required_nested(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT JSON_QUERY([r].[OptionalRelated], '$.RequiredNested') +FROM [RootEntity] AS [r] +"""); + } + + public override async Task Select_optional_related_optional_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_optional_related_optional_nested(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT JSON_QUERY([r].[OptionalRelated], '$.OptionalNested') +FROM [RootEntity] AS [r] +"""); + } + + #endregion Non-collection + + #region Collection + + public override async Task Select_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_related_collection(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT [r].[RelatedCollection] +FROM [RootEntity] AS [r] +ORDER BY [r].[Id] +"""); + } + + public override async Task Select_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_required_related_nested_collection(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT JSON_QUERY([r].[RequiredRelated], '$.NestedCollection') +FROM [RootEntity] AS [r] +"""); + } + + public override async Task Select_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_optional_related_nested_collection(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT JSON_QUERY([r].[OptionalRelated], '$.NestedCollection') +FROM [RootEntity] AS [r] +"""); + } + + public override async Task SelectMany_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.SelectMany_related_collection(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String], [r0].[NestedCollection], [r0].[OptionalNested], [r0].[RequiredNested] +FROM [RootEntity] AS [r] +CROSS APPLY OPENJSON([r].[RelatedCollection], '$') WITH ( + [Id] int '$.Id', + [Int] int '$.Int', + [Name] nvarchar(max) '$.Name', + [String] nvarchar(max) '$.String', + [NestedCollection] nvarchar(max) '$.NestedCollection' AS JSON, + [OptionalNested] nvarchar(max) '$.OptionalNested' AS JSON, + [RequiredNested] nvarchar(max) '$.RequiredNested' AS JSON +) AS [r0] +"""); + } + + public override async Task SelectMany_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.SelectMany_required_related_nested_collection(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT [n].[Id], [n].[Int], [n].[Name], [n].[String] +FROM [RootEntity] AS [r] +CROSS APPLY OPENJSON([r].[RequiredRelated], '$.NestedCollection') WITH ( + [Id] int '$.Id', + [Int] int '$.Int', + [Name] nvarchar(max) '$.Name', + [String] nvarchar(max) '$.String' +) AS [n] +"""); + } + + public override async Task SelectMany_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.SelectMany_optional_related_nested_collection(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT [n].[Id], [n].[Int], [n].[Name], [n].[String] +FROM [RootEntity] AS [r] +CROSS APPLY OPENJSON([r].[OptionalRelated], '$.NestedCollection') WITH ( + [Id] int '$.Id', + [Int] int '$.Int', + [Name] nvarchar(max) '$.Name', + [String] nvarchar(max) '$.String' +) AS [n] +"""); + } + + #endregion Collection + + #region Multiple + + public override async Task Select_root_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_root_duplicated(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +"""); + } + + #endregion Multiple + + #region Subquery + + public override async Task Select_subquery_required_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_subquery_required_related_FirstOrDefault(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT [r1].[c] +FROM [RootEntity] AS [r] +OUTER APPLY ( + SELECT TOP(1) JSON_QUERY([r0].[RequiredRelated], '$.RequiredNested') AS [c] + FROM [RootEntity] AS [r0] + ORDER BY [r0].[Id] +) AS [r1] +"""); + } + + public override async Task Select_subquery_optional_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_subquery_optional_related_FirstOrDefault(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT [r1].[c] +FROM [RootEntity] AS [r] +OUTER APPLY ( + SELECT TOP(1) JSON_QUERY([r0].[OptionalRelated], '$.RequiredNested') AS [c] + FROM [RootEntity] AS [r0] + ORDER BY [r0].[Id] +) AS [r1] +"""); + } + + #endregion Subquery + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonSqlServerFixture.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonSqlServerFixture.cs new file mode 100644 index 00000000000..ea3264387b4 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonSqlServerFixture.cs @@ -0,0 +1,105 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexJson; + +public class ComplexJsonSqlServerFixture : ComplexJsonRelationalFixtureBase +{ + protected override ITestStoreFactory TestStoreFactory + => SqlServerTestStoreFactory.Instance; + + protected override async Task SeedAsync(PoolableDbContext context) + { + // TODO: Temporary, until we have update pipeline support for complex JSON + await context.Database.ExecuteSqlAsync($$$""" +INSERT INTO RootEntity (Id, Name, RequiredRelated, OptionalRelated, RelatedCollection) VALUES +( + 1, + 'Root1', + -- RequiredRelated: + '{ + "Id": 100, + "Name": "Root1_RequiredRelated", + "Int": 8, + "String": "foo", + "RequiredNested": { "Id": 1000, "Name": "Root1_RequiredRelated_RequiredNested", "Int": 50, "String": "foo_foo" }, + "OptionalNested": { "Id": 1001, "Name": "Root1_RequiredRelated_OptionalNested", "Int": 51, "String": "foo_bar" }, + "NestedCollection": + [ + { "Id": 1002, "Name": "Root1_RequiredRelated_NestedCollection_1", "Int": 52, "String": "foo_baz1" }, + { "Id": 1003, "Name": "Root1_RequiredRelated_NestedCollection_2", "Int": 53, "String": "foo_baz2" } + ] + }', + -- OptionalRelated: + '{ + "Id": 101, + "Name": "Root1_OptionalRelated", + "Int": 9, + "String": "bar", + "RequiredNested": { "Id": 1010, "Name": "Root1_OptionalRelated_RequiredNested", "Int": 52, "String": "bar_foo" }, + "OptionalNested": { "Id": 1011, "Name": "Root1_OptionalRelated_OptionalNested", "Int": 53, "String": "bar_bar" }, + "NestedCollection": + [ + { "Id": 1012, "Name": "Root1_OptionalRelated_NestedCollection_1", "Int": 54, "String": "bar_baz1" }, + { "Id": 1013, "Name": "Root1_OptionalRelated_NestedCollection_2", "Int": 55, "String": "bar_baz2" } + ] + }', + -- RelatedCollection: + '[ + { + "Id": 102, + "Name": "Root1_RelatedCollection_1", + "Int": 21, + "String": "foo", + "RequiredNested": { "Id": 1020, "Name": "Root1_RelatedCollection_1_RequiredNested", "Int": 50, "String": "foo_foo" }, + "OptionalNested": { "Id": 1021, "Name": "Root1_RelatedCollection_1_OptionalNested", "Int": 51, "String": "foo_bar" }, + "NestedCollection": + [ + { "Id": 1022, "Name": "Root1_RelatedCollection_1_NestedCollection_1", "Int": 53, "String": "foo_bar" }, + { "Id": 1023, "Name": "Root1_RelatedCollection_1_NestedCollection_2", "Int": 51, "String": "foo_bar" } + ] + }, + { + "Id": 103, + "Name": "Root1_RelatedCollection_2", + "Int": 22, + "String": "foo", + "RequiredNested": { "Id": 1030, "Name": "Root1_RelatedCollection_2_RequiredNested", "Int": 50, "String": "foo_foo" }, + "OptionalNested": { "Id": 1031, "Name": "Root1_RelatedCollection_2_OptionalNested", "Int": 51, "String": "foo_bar" }, + "NestedCollection": + [ + { "Id": 1032, "Name": "Root1_RelatedCollection_2_NestedCollection_1", "Int": 53, "String": "foo_bar" }, + { "Id": 1033, "Name": "Root1_RelatedCollection_2_NestedCollection_2", "Int": 51, "String": "foo_bar" } + ] + } + ]' +), +( + 2, + 'Root2', + -- RequiredRelated: + '{ + "Id": 200, + "Name": "Root2_RequiredRelated", + "Int": 10, + "String": "aaa", + "RequiredNested": { "Id": 2000, "Name": "Root2_RequiredRelated_RequiredNested", "Int": 54, "String": "aaa_xxx" }, + "OptionalNested": { "Id": 2001, "Name": "Root2_RequiredRelated_OptionalNested", "Int": 55, "String": "aaa_yyy" }, + "NestedCollection": [] + }', + -- OptionalRelated: + '{ + "Id": 201, + "Name": "Root2_OptionalRelated", + "Int": 11, + "String": "bbb", + "RequiredNested": { "Id": 2010, "Name": "Root2_OptionalRelated_RequiredNested", "Int": 56, "String": "bbb_xxx" }, + "OptionalNested": { "Id": 2011, "Name": "Root2_OptionalRelated_OptionalNested", "Int": 57, "String": "bbb_yyy" }, + "NestedCollection": [] + }', + -- RelatedCollection: + '[]' +) +"""); + } +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingMiscellaneousSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingMiscellaneousSqlServerTest.cs new file mode 100644 index 00000000000..dd1a30ae2c8 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingMiscellaneousSqlServerTest.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexTableSplitting; + +public class ComplexTableSplittingMiscellaneousSqlServerTest( + ComplexTableSplittingSqlServerFixture fixture, + ITestOutputHelper testOutputHelper) + : ComplexTableSplittingMiscellaneousRelationalTestBase(fixture, testOutputHelper) +{ + #region Simple filters + + public override async Task Where_related_property(bool async) + { + await base.Where_related_property(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] +WHERE [r].[RequiredRelated_Int] = 8 +"""); + } + + public override async Task Where_optional_related_property(bool async) + { + await base.Where_optional_related_property(async); + + AssertSql(); + } + + public override async Task Where_nested_related_property(bool async) + { + await base.Where_nested_related_property(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] +WHERE [r].[RequiredRelated_RequiredNested_Int] = 50 +"""); + } + + #endregion Simple filters + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingProjectionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingProjectionSqlServerTest.cs index 7ae2df9b88f..dcffc24ab0d 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingProjectionSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingProjectionSqlServerTest.cs @@ -3,242 +3,231 @@ namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexTableSplitting; -public class ComplexTableSplittingProjectionSqlServerTest - : ComplexTableSplittingProjectionRelationalTestBase +public class ComplexTableSplittingProjectionSqlServerTest( + ComplexTableSplittingSqlServerFixture fixture, + ITestOutputHelper testOutputHelper) + : ComplexTableSplittingProjectionRelationalTestBase(fixture, testOutputHelper) { - public ComplexTableSplittingProjectionSqlServerTest(ComplexTableSplittingSqlServerFixture fixture, ITestOutputHelper testOutputHelper) - : base(fixture) - { - Fixture.TestSqlLoggerFactory.Clear(); - Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); - } - public override async Task Select_root(bool async, QueryTrackingBehavior queryTrackingBehavior) { await base.Select_root(async, queryTrackingBehavior); AssertSql( """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId] -FROM [RootEntities] AS [r] +SELECT [r].[Id], [r].[Name], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] """); } - public override async Task Select_trunk_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_trunk_optional(async, queryTrackingBehavior); + #region Simple properties - AssertSql( - """ -SELECT [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId], [t].[RequiredReferenceBranch_Name], [t].[RequiredReferenceBranch_RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -LEFT JOIN [TrunkEntities] AS [t] ON [r].[OptionalReferenceTrunkId] = [t].[Id] -ORDER BY [r].[Id] -"""); - } - - public override async Task Select_trunk_required(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_related_property(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_trunk_required(async, queryTrackingBehavior); + await base.Select_related_property(async, queryTrackingBehavior); AssertSql( """ -SELECT [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId], [t].[RequiredReferenceBranch_Name], [t].[RequiredReferenceBranch_RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] -ORDER BY [r].[Id] +SELECT [r].[RequiredRelated_String] +FROM [RootEntity] AS [r] """); } - public override async Task Select_trunk_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_property(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_trunk_collection(async, queryTrackingBehavior); + await base.Select_optional_related_property(async, queryTrackingBehavior); AssertSql( """ -SELECT [r].[Id], [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId], [t].[RequiredReferenceBranch_Name], [t].[RequiredReferenceBranch_RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -LEFT JOIN [TrunkEntities] AS [t] ON [r].[Id] = [t].[CollectionRootId] -ORDER BY [r].[Id] +SELECT [r].[Id], [r].[Name], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] """); } - public override async Task Select_branch_required_required(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_property_value_type(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_branch_required_required(async, queryTrackingBehavior); + await base.Select_optional_related_property_value_type(async, queryTrackingBehavior); AssertSql( """ -SELECT [t].[RequiredReferenceBranch_Name], [t].[RequiredReferenceBranch_RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] -ORDER BY [r].[Id] +SELECT [r].[Id], [r].[Name], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] """); } - public override async Task Select_branch_required_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_branch_required_optional(async, queryTrackingBehavior); - - AssertSql(); - } - - public override async Task Select_branch_optional_required(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_branch_optional_required(async, queryTrackingBehavior); + #endregion Simple properties - AssertSql(); - } + #region Non-collection - public override async Task Select_branch_optional_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_related(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_branch_optional_optional(async, queryTrackingBehavior); + await base.Select_related(async, queryTrackingBehavior); - AssertSql(); - } - - public override async Task Select_branch_required_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_branch_required_collection(async, queryTrackingBehavior); - - AssertSql(); - } - - public override async Task Select_branch_optional_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_branch_optional_collection(async, queryTrackingBehavior); - - AssertSql(); + AssertSql( + """ +SELECT [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] +"""); } - #region Multiple - - public override async Task Select_root_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_root_duplicated(async, queryTrackingBehavior); + await base.Select_optional_related(async, queryTrackingBehavior); AssertSql( """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId] -FROM [RootEntities] AS [r] +SELECT [r].[Id], [r].[Name], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] """); } - public override async Task Select_trunk_and_branch_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_required_related_required_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_trunk_and_branch_duplicated(async, queryTrackingBehavior); + await base.Select_required_related_required_nested(async, queryTrackingBehavior); - AssertSql(); + AssertSql( + """ +SELECT [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] +"""); } - public override async Task Select_trunk_and_trunk_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_required_related_optional_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_trunk_and_trunk_duplicated(async, queryTrackingBehavior); + await base.Select_required_related_optional_nested(async, queryTrackingBehavior); - AssertSql(); + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] +"""); } - public override async Task Select_leaf_trunk_root(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_required_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_leaf_trunk_root(async, queryTrackingBehavior); + await base.Select_optional_related_required_nested(async, queryTrackingBehavior); AssertSql( """ -SELECT [t].[RequiredReferenceBranch_RequiredReferenceLeaf_Name], [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId], [t].[RequiredReferenceBranch_Name], [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] +SELECT [r].[Id], [r].[Name], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] """); } - public override async Task Select_multiple_branch_leaf(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_optional_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_multiple_branch_leaf(async, queryTrackingBehavior); + await base.Select_optional_related_optional_nested(async, queryTrackingBehavior); - AssertSql(); + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] +"""); } - #endregion Multiple + #endregion Non-collection - #region Subquery + #region Collection - public override async Task Select_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_subquery_root_set_required_trunk_FirstOrDefault_branch(async, queryTrackingBehavior); + await base.Select_related_collection(async, queryTrackingBehavior); - AssertSql(); + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] +ORDER BY [r].[Id] +"""); } - public override async Task Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(async, queryTrackingBehavior); + await base.Select_required_related_nested_collection(async, queryTrackingBehavior); - AssertSql(); + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] +"""); } - public override async Task Select_everything(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_everything(async, queryTrackingBehavior); + await base.Select_optional_related_nested_collection(async, queryTrackingBehavior); AssertSql( -""" -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId], [t].[RequiredReferenceBranch_Name], [t].[RequiredReferenceBranch_RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[Id] = [t].[Id] + """ +SELECT [r].[Id], [r].[Name], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] """); } - public override async Task Select_subquery_root_set_trunk_FirstOrDefault_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task SelectMany_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_subquery_root_set_trunk_FirstOrDefault_collection(async, queryTrackingBehavior); + await base.SelectMany_related_collection(async, queryTrackingBehavior); AssertSql(); } - public override async Task Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task SelectMany_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(async, queryTrackingBehavior); + await base.SelectMany_required_related_nested_collection(async, queryTrackingBehavior); AssertSql(); } - public override async Task Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task SelectMany_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(async, queryTrackingBehavior); + await base.SelectMany_optional_related_nested_collection(async, queryTrackingBehavior); AssertSql(); } - #endregion Subquery + #endregion Collection - #region SelectMany + #region Multiple - public override async Task SelectMany_trunk_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_root_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.SelectMany_trunk_collection(async, queryTrackingBehavior); + await base.Select_root_duplicated(async, queryTrackingBehavior); - AssertSql(); + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] +"""); } - public override async Task SelectMany_required_trunk_reference_branch_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + #endregion Multiple + + #region Subquery + + public override async Task Select_subquery_required_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.SelectMany_required_trunk_reference_branch_collection(async, queryTrackingBehavior); + await base.Select_subquery_required_related_FirstOrDefault(async, queryTrackingBehavior); AssertSql(); } - public override async Task SelectMany_optional_trunk_reference_branch_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_subquery_optional_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.SelectMany_optional_trunk_reference_branch_collection(async, queryTrackingBehavior); + await base.Select_subquery_optional_related_FirstOrDefault(async, queryTrackingBehavior); - AssertSql(); + AssertSql( + """ +SELECT [r1].[Id], [r1].[Name], [r1].[RequiredRelated_Id], [r1].[RequiredRelated_Int], [r1].[RequiredRelated_Name], [r1].[RequiredRelated_String], [r1].[RequiredRelated_RequiredNested_Id], [r1].[RequiredRelated_RequiredNested_Int], [r1].[RequiredRelated_RequiredNested_Name], [r1].[RequiredRelated_RequiredNested_String], [r1].[c] +FROM [RootEntity] AS [r] +OUTER APPLY ( + SELECT TOP(1) [r0].[Id], [r0].[Name], [r0].[RequiredRelated_Id], [r0].[RequiredRelated_Int], [r0].[RequiredRelated_Name], [r0].[RequiredRelated_String], [r0].[RequiredRelated_RequiredNested_Id], [r0].[RequiredRelated_RequiredNested_Int], [r0].[RequiredRelated_RequiredNested_Name], [r0].[RequiredRelated_RequiredNested_String], 1 AS [c] + FROM [RootEntity] AS [r0] + ORDER BY [r0].[Id] +) AS [r1] +"""); } - #endregion SelectMany + #endregion Subquery [ConditionalFact] public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); - - private void AssertSql(params string[] expected) - => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingSqlServerFixture.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingSqlServerFixture.cs index 329835b86a4..a016d28fc69 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingSqlServerFixture.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingSqlServerFixture.cs @@ -7,7 +7,4 @@ public class ComplexTableSplittingSqlServerFixture : ComplexTableSplittingRelati { protected override ITestStoreFactory TestStoreFactory => SqlServerTestStoreFactory.Instance; - - public TestSqlLoggerFactory TestSqlLoggerFactory - => (TestSqlLoggerFactory)ListLoggerFactory; } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/JsonOwnedNavigations/JsonOwnedNavigationsProjectionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/JsonOwnedNavigations/JsonOwnedNavigationsProjectionSqlServerTest.cs deleted file mode 100644 index 58678a262d2..00000000000 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/JsonOwnedNavigations/JsonOwnedNavigationsProjectionSqlServerTest.cs +++ /dev/null @@ -1,460 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.EntityFrameworkCore.Query.Relationships.JsonOwnedNavigations; - -public class JsonOwnedNavigationsProjectionSqlServerTest - : JsonOwnedNavigationsProjectionRelationalTestBase -{ - public JsonOwnedNavigationsProjectionSqlServerTest(JsonOwnedNavigationsSqlServerFixture fixture, ITestOutputHelper testOutputHelper) - : base(fixture) - { - Fixture.TestSqlLoggerFactory.Clear(); - Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); - } - - public override async Task Select_root(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_root(async, queryTrackingBehavior); - - AssertSql( - """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [r].[CollectionTrunk], [r].[OptionalReferenceTrunk], [r].[RequiredReferenceTrunk] -FROM [RootEntities] AS [r] -"""); - } - - public override async Task Select_trunk_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_trunk_optional(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r].[OptionalReferenceTrunk], [r].[Id] -FROM [RootEntities] AS [r] -ORDER BY [r].[Id] -"""); - } - } - - public override async Task Select_trunk_required(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_trunk_required(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r].[RequiredReferenceTrunk], [r].[Id] -FROM [RootEntities] AS [r] -ORDER BY [r].[Id] -"""); - } - } - - public override async Task Select_trunk_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_trunk_collection(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r].[CollectionTrunk], [r].[Id] -FROM [RootEntities] AS [r] -ORDER BY [r].[Id] -"""); - } - } - - public override async Task Select_branch_required_required(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_branch_required_required(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT JSON_QUERY([r].[RequiredReferenceTrunk], '$.RequiredReferenceBranch'), [r].[Id] -FROM [RootEntities] AS [r] -ORDER BY [r].[Id] -"""); - } - } - - public override async Task Select_branch_required_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_branch_required_optional(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT JSON_QUERY([r].[RequiredReferenceTrunk], '$.OptionalReferenceBranch'), [r].[Id] -FROM [RootEntities] AS [r] -ORDER BY [r].[Id] -"""); - } - } - - public override async Task Select_branch_optional_required(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_branch_optional_required(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT JSON_QUERY([r].[RequiredReferenceTrunk], '$.RequiredReferenceBranch'), [r].[Id] -FROM [RootEntities] AS [r] -ORDER BY [r].[Id] -"""); - } - } - - public override async Task Select_branch_optional_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_branch_optional_optional(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT JSON_QUERY([r].[RequiredReferenceTrunk], '$.OptionalReferenceBranch'), [r].[Id] -FROM [RootEntities] AS [r] -ORDER BY [r].[Id] -"""); - } - } - - public override async Task Select_branch_required_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_branch_required_collection(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT JSON_QUERY([r].[RequiredReferenceTrunk], '$.CollectionBranch'), [r].[Id] -FROM [RootEntities] AS [r] -ORDER BY [r].[Id] -"""); - } - } - - public override async Task Select_branch_optional_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_branch_optional_collection(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT JSON_QUERY([r].[RequiredReferenceTrunk], '$.CollectionBranch'), [r].[Id] -FROM [RootEntities] AS [r] -ORDER BY [r].[Id] -"""); - } - } - - public override async Task Select_multiple_branch_leaf(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_multiple_branch_leaf(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r].[Id], JSON_QUERY([r].[RequiredReferenceTrunk], '$.RequiredReferenceBranch'), JSON_QUERY([r].[RequiredReferenceTrunk], '$.RequiredReferenceBranch.OptionalReferenceLeaf'), JSON_QUERY([r].[RequiredReferenceTrunk], '$.RequiredReferenceBranch.CollectionLeaf'), JSON_QUERY([r].[RequiredReferenceTrunk], '$.CollectionBranch'), JSON_VALUE([r].[RequiredReferenceTrunk], '$.RequiredReferenceBranch.OptionalReferenceLeaf.Name') -FROM [RootEntities] AS [r] -"""); - } - } - - #region Multiple - - public override async Task Select_root_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_root_duplicated(async, queryTrackingBehavior); - - AssertSql( - """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [r].[CollectionTrunk], [r].[OptionalReferenceTrunk], [r].[RequiredReferenceTrunk], [r].[CollectionTrunk], [r].[OptionalReferenceTrunk], [r].[RequiredReferenceTrunk] -FROM [RootEntities] AS [r] -"""); - } - - public override async Task Select_trunk_and_branch_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_trunk_and_branch_duplicated(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r].[OptionalReferenceTrunk], [r].[Id], JSON_QUERY([r].[OptionalReferenceTrunk], '$.RequiredReferenceBranch'), [r].[OptionalReferenceTrunk], JSON_QUERY([r].[OptionalReferenceTrunk], '$.RequiredReferenceBranch') -FROM [RootEntities] AS [r] -ORDER BY [r].[Id] -"""); - } - } - - public override async Task Select_trunk_and_trunk_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_trunk_and_trunk_duplicated(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r].[RequiredReferenceTrunk], [r].[Id], JSON_QUERY([r].[RequiredReferenceTrunk], '$.OptionalReferenceBranch.RequiredReferenceLeaf'), [r].[RequiredReferenceTrunk], JSON_QUERY([r].[RequiredReferenceTrunk], '$.OptionalReferenceBranch.RequiredReferenceLeaf') -FROM [RootEntities] AS [r] -ORDER BY [r].[Id] -"""); - } - } - - public override async Task Select_leaf_trunk_root(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_leaf_trunk_root(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT JSON_QUERY([r].[RequiredReferenceTrunk], '$.RequiredReferenceBranch.RequiredReferenceLeaf'), [r].[Id], [r].[RequiredReferenceTrunk], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [r].[CollectionTrunk], [r].[OptionalReferenceTrunk], [r].[RequiredReferenceTrunk] -FROM [RootEntities] AS [r] -"""); - } - } - - #endregion Multiple - - #region Subquery - - public override async Task Select_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_leaf_trunk_root(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT JSON_QUERY([r].[RequiredReferenceTrunk], '$.RequiredReferenceBranch.RequiredReferenceLeaf'), [r].[Id], [r].[RequiredReferenceTrunk], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [r].[CollectionTrunk], [r].[OptionalReferenceTrunk], [r].[RequiredReferenceTrunk] -FROM [RootEntities] AS [r] -"""); - } - } - - public override async Task Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_leaf_trunk_root(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT JSON_QUERY([r].[RequiredReferenceTrunk], '$.RequiredReferenceBranch.RequiredReferenceLeaf'), [r].[Id], [r].[RequiredReferenceTrunk], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [r].[CollectionTrunk], [r].[OptionalReferenceTrunk], [r].[RequiredReferenceTrunk] -FROM [RootEntities] AS [r] -"""); - } - } - - public override async Task Select_subquery_root_set_trunk_FirstOrDefault_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_leaf_trunk_root(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT JSON_QUERY([r].[RequiredReferenceTrunk], '$.RequiredReferenceBranch.RequiredReferenceLeaf'), [r].[Id], [r].[RequiredReferenceTrunk], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [r].[CollectionTrunk], [r].[OptionalReferenceTrunk], [r].[RequiredReferenceTrunk] -FROM [RootEntities] AS [r] -"""); - } - } - - public override async Task Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_leaf_trunk_root(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT JSON_QUERY([r].[RequiredReferenceTrunk], '$.RequiredReferenceBranch.RequiredReferenceLeaf'), [r].[Id], [r].[RequiredReferenceTrunk], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [r].[CollectionTrunk], [r].[OptionalReferenceTrunk], [r].[RequiredReferenceTrunk] -FROM [RootEntities] AS [r] -"""); - } - } - - public override async Task Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_leaf_trunk_root(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT JSON_QUERY([r].[RequiredReferenceTrunk], '$.RequiredReferenceBranch.RequiredReferenceLeaf'), [r].[Id], [r].[RequiredReferenceTrunk], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [r].[CollectionTrunk], [r].[OptionalReferenceTrunk], [r].[RequiredReferenceTrunk] -FROM [RootEntities] AS [r] -"""); - } - } - - #endregion Subquery - - #region SelectMany - - public override async Task SelectMany_trunk_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_leaf_trunk_root(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT JSON_QUERY([r].[RequiredReferenceTrunk], '$.RequiredReferenceBranch.RequiredReferenceLeaf'), [r].[Id], [r].[RequiredReferenceTrunk], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [r].[CollectionTrunk], [r].[OptionalReferenceTrunk], [r].[RequiredReferenceTrunk] -FROM [RootEntities] AS [r] -"""); - } - } - - public override async Task SelectMany_required_trunk_reference_branch_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_leaf_trunk_root(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT JSON_QUERY([r].[RequiredReferenceTrunk], '$.RequiredReferenceBranch.RequiredReferenceLeaf'), [r].[Id], [r].[RequiredReferenceTrunk], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [r].[CollectionTrunk], [r].[OptionalReferenceTrunk], [r].[RequiredReferenceTrunk] -FROM [RootEntities] AS [r] -"""); - } - } - - public override async Task SelectMany_optional_trunk_reference_branch_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_leaf_trunk_root(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT JSON_QUERY([r].[RequiredReferenceTrunk], '$.RequiredReferenceBranch.RequiredReferenceLeaf'), [r].[Id], [r].[RequiredReferenceTrunk], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [r].[CollectionTrunk], [r].[OptionalReferenceTrunk], [r].[RequiredReferenceTrunk] -FROM [RootEntities] AS [r] -"""); - } - } - - #endregion SelectMany - - private async Task AssertCantTrackJson(QueryTrackingBehavior queryTrackingBehavior, Func test) - { - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - var message = (await Assert.ThrowsAsync(test)).Message; - - Assert.Equal(CoreStrings.OwnedEntitiesCannotBeTrackedWithoutTheirOwner, message); - AssertSql(); - - return; - } - - await test(); - } - - [ConditionalFact] - public virtual void Check_all_tests_overridden() - => TestHelpers.AssertAllMethodsOverridden(GetType()); - - private void AssertSql(params string[] expected) - => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); -} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/JsonOwnedNavigations/JsonOwnedNavigationsSqlServerFixture.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/JsonOwnedNavigations/JsonOwnedNavigationsSqlServerFixture.cs deleted file mode 100644 index 5684c421624..00000000000 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/JsonOwnedNavigations/JsonOwnedNavigationsSqlServerFixture.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.EntityFrameworkCore.Query.Relationships.JsonOwnedNavigations; - -public class JsonOwnedNavigationsSqlServerFixture : JsonOwnedNavigationsRelationalFixtureBase, ITestSqlLoggerFactory -{ - protected override ITestStoreFactory TestStoreFactory - => SqlServerTestStoreFactory.Instance; - - public TestSqlLoggerFactory TestSqlLoggerFactory - => (TestSqlLoggerFactory)ListLoggerFactory; -} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/JsonOwnedNavigations/JsonOwnedTypeSqlServerFixture.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/JsonOwnedNavigations/JsonOwnedTypeSqlServerFixture.cs deleted file mode 100644 index db8839daa5c..00000000000 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/JsonOwnedNavigations/JsonOwnedTypeSqlServerFixture.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.EntityFrameworkCore.TestModels.RelationshipsModel; - -namespace Microsoft.EntityFrameworkCore.Query.Relationships.JsonOwnedNavigations; - -public class JsonOwnedTypeSqlServerFixture : JsonOwnedNavigationsRelationalFixtureBase, ITestSqlLoggerFactory -{ - protected override string StoreName => "JsonTypeRelationshipsQueryTest"; - - protected override ITestStoreFactory TestStoreFactory - => SqlServerTestStoreFactory.Instance; - - public TestSqlLoggerFactory TestSqlLoggerFactory - => (TestSqlLoggerFactory)ListLoggerFactory; - - protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) - { - base.OnModelCreating(modelBuilder, context); - - modelBuilder.Entity().OwnsOne(x => x.RequiredReferenceTrunk).HasColumnType("json"); - modelBuilder.Entity().OwnsOne(x => x.OptionalReferenceTrunk).HasColumnType("json"); - modelBuilder.Entity().OwnsMany(x => x.CollectionTrunk).HasColumnType("json"); - } - - public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) - => base.AddOptions(builder.ConfigureWarnings(b => b.Ignore(SqlServerEventId.JsonTypeExperimental))); -} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/Navigations/NavigationsCollectionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/Navigations/NavigationsCollectionSqlServerTest.cs new file mode 100644 index 00000000000..0235781ccb8 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/Navigations/NavigationsCollectionSqlServerTest.cs @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; + +public class NavigationsCollectionSqlServerTest(NavigationsSqlServerFixture fixture, ITestOutputHelper testOutputHelper) + : NavigationsCollectionRelationalTestBase(fixture, testOutputHelper) +{ + public override async Task Count(bool async) + { + await base.Count(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelatedId], [r].[RequiredRelatedId] +FROM [RootEntity] AS [r] +WHERE ( + SELECT COUNT(*) + FROM [RelatedType] AS [r0] + WHERE [r].[Id] = [r0].[CollectionRootId]) = 2 +"""); + } + + public override async Task Where(bool async) + { + await base.Where(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelatedId], [r].[RequiredRelatedId] +FROM [RootEntity] AS [r] +WHERE ( + SELECT COUNT(*) + FROM [RelatedType] AS [r0] + WHERE [r].[Id] = [r0].[CollectionRootId] AND [r0].[Int] <> 50) = 2 +"""); + } + + public override async Task OrderBy_ElementAt(bool async) + { + await base.OrderBy_ElementAt(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelatedId], [r].[RequiredRelatedId] +FROM [RootEntity] AS [r] +WHERE ( + SELECT [r0].[Int] + FROM [RelatedType] AS [r0] + WHERE [r].[Id] = [r0].[CollectionRootId] + ORDER BY [r0].[Id] + OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) = 21 +"""); + } + + public override async Task Index_constant(bool async) + { + await base.Index_constant(async); + + AssertSql(); + } + + public override async Task Index_parameter(bool async) + { + await base.Index_parameter(async); + + AssertSql(); + } + + public override async Task Index_column(bool async) + { + await base.Index_column(async); + + AssertSql(); + } + + public override async Task Index_out_of_bounds(bool async) + { + await base.Index_out_of_bounds(async); + + AssertSql(); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/Navigations/NavigationsIncludeSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/Navigations/NavigationsIncludeSqlServerTest.cs index 1f9d4be5fd3..fb1c3e89d8c 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/Navigations/NavigationsIncludeSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/Navigations/NavigationsIncludeSqlServerTest.cs @@ -3,134 +3,127 @@ namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; -public class NavigationsIncludeSqlServerTest - : NavigationsIncludeRelationalTestBase +public class NavigationsIncludeSqlServerTest(NavigationsSqlServerFixture fixture, ITestOutputHelper testOutputHelper) + : NavigationsIncludeRelationalTestBase(fixture, testOutputHelper) { - public NavigationsIncludeSqlServerTest(NavigationsSqlServerFixture fixture, ITestOutputHelper testOutputHelper) - : base(fixture) + public override async Task Include_required(bool async) { - Fixture.TestSqlLoggerFactory.Clear(); - Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); - } - - public override async Task Include_trunk_required(bool async) - { - await base.Include_trunk_required(async); + await base.Include_required(async); AssertSql( """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] +SELECT [r].[Id], [r].[Name], [r].[OptionalRelatedId], [r].[RequiredRelatedId], [r0].[Id], [r0].[CollectionRootId], [r0].[Int], [r0].[Name], [r0].[OptionalNestedId], [r0].[RequiredNestedId], [r0].[String] +FROM [RootEntity] AS [r] +INNER JOIN [RelatedType] AS [r0] ON [r].[RequiredRelatedId] = [r0].[Id] """); } - public override async Task Include_trunk_optional(bool async) + public override async Task Include_optional(bool async) { - await base.Include_trunk_optional(async); + await base.Include_optional(async); AssertSql( -""" -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId] -FROM [RootEntities] AS [r] -LEFT JOIN [TrunkEntities] AS [t] ON [r].[OptionalReferenceTrunkId] = [t].[Id] + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelatedId], [r].[RequiredRelatedId], [r0].[Id], [r0].[CollectionRootId], [r0].[Int], [r0].[Name], [r0].[OptionalNestedId], [r0].[RequiredNestedId], [r0].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RelatedType] AS [r0] ON [r].[OptionalRelatedId] = [r0].[Id] """); } - public override async Task Include_trunk_collection(bool async) + public override async Task Include_collection(bool async) { - await base.Include_trunk_collection(async); + await base.Include_collection(async); AssertSql( """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId] -FROM [RootEntities] AS [r] -LEFT JOIN [TrunkEntities] AS [t] ON [r].[Id] = [t].[CollectionRootId] +SELECT [r].[Id], [r].[Name], [r].[OptionalRelatedId], [r].[RequiredRelatedId], [r0].[Id], [r0].[CollectionRootId], [r0].[Int], [r0].[Name], [r0].[OptionalNestedId], [r0].[RequiredNestedId], [r0].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RelatedType] AS [r0] ON [r].[Id] = [r0].[CollectionRootId] ORDER BY [r].[Id] """); } - public override async Task Include_trunk_required_optional_and_collection(bool async) + public override async Task Include_required_optional_and_collection(bool async) { - await base.Include_trunk_required_optional_and_collection(async); + await base.Include_required_optional_and_collection(async); AssertSql( """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId], [t0].[Id], [t0].[CollectionRootId], [t0].[Name], [t0].[OptionalReferenceBranchId], [t0].[RequiredReferenceBranchId], [t1].[Id], [t1].[CollectionRootId], [t1].[Name], [t1].[OptionalReferenceBranchId], [t1].[RequiredReferenceBranchId] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] -LEFT JOIN [TrunkEntities] AS [t0] ON [r].[OptionalReferenceTrunkId] = [t0].[Id] -LEFT JOIN [TrunkEntities] AS [t1] ON [r].[Id] = [t1].[CollectionRootId] -ORDER BY [r].[Id], [t].[Id], [t0].[Id] +SELECT [r].[Id], [r].[Name], [r].[OptionalRelatedId], [r].[RequiredRelatedId], [r0].[Id], [r0].[CollectionRootId], [r0].[Int], [r0].[Name], [r0].[OptionalNestedId], [r0].[RequiredNestedId], [r0].[String], [r1].[Id], [r1].[CollectionRootId], [r1].[Int], [r1].[Name], [r1].[OptionalNestedId], [r1].[RequiredNestedId], [r1].[String], [r2].[Id], [r2].[CollectionRootId], [r2].[Int], [r2].[Name], [r2].[OptionalNestedId], [r2].[RequiredNestedId], [r2].[String] +FROM [RootEntity] AS [r] +INNER JOIN [RelatedType] AS [r0] ON [r].[RequiredRelatedId] = [r0].[Id] +LEFT JOIN [RelatedType] AS [r1] ON [r].[OptionalRelatedId] = [r1].[Id] +LEFT JOIN [RelatedType] AS [r2] ON [r].[Id] = [r2].[CollectionRootId] +ORDER BY [r].[Id], [r0].[Id], [r1].[Id] """); } - public override async Task Include_branch_required_required(bool async) + public override async Task Include_nested(bool async) { - await base.Include_branch_required_required(async); + await base.Include_nested(async); AssertSql( """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId], [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] -INNER JOIN [BranchEntities] AS [b] ON [t].[RequiredReferenceBranchId] = [b].[Id] +SELECT [r].[Id], [r].[Name], [r].[OptionalRelatedId], [r].[RequiredRelatedId], [r0].[Id], [r0].[CollectionRootId], [r0].[Int], [r0].[Name], [r0].[OptionalNestedId], [r0].[RequiredNestedId], [r0].[String], [n].[Id], [n].[CollectionRelatedId], [n].[Int], [n].[Name], [n].[String] +FROM [RootEntity] AS [r] +INNER JOIN [RelatedType] AS [r0] ON [r].[RequiredRelatedId] = [r0].[Id] +INNER JOIN [NestedType] AS [n] ON [r0].[RequiredNestedId] = [n].[Id] """); } - public override async Task Include_branch_required_collection(bool async) + public override async Task Include_nested_optional(bool async) { - await base.Include_branch_required_collection(async); + await base.Include_nested_optional(async); AssertSql( """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId], [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] -LEFT JOIN [BranchEntities] AS [b] ON [t].[Id] = [b].[CollectionTrunkId] -ORDER BY [r].[Id], [t].[Id] +SELECT [r].[Id], [r].[Name], [r].[OptionalRelatedId], [r].[RequiredRelatedId], [r0].[Id], [r0].[CollectionRootId], [r0].[Int], [r0].[Name], [r0].[OptionalNestedId], [r0].[RequiredNestedId], [r0].[String], [n].[Id], [n].[CollectionRelatedId], [n].[Int], [n].[Name], [n].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RelatedType] AS [r0] ON [r].[OptionalRelatedId] = [r0].[Id] +LEFT JOIN [NestedType] AS [n] ON [r0].[OptionalNestedId] = [n].[Id] """); } - public override async Task Include_branch_optional_optional(bool async) + public override async Task Include_nested_collection(bool async) { - await base.Include_branch_optional_optional(async); + await base.Include_nested_collection(async); AssertSql( """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId], [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId] -FROM [RootEntities] AS [r] -LEFT JOIN [TrunkEntities] AS [t] ON [r].[OptionalReferenceTrunkId] = [t].[Id] -LEFT JOIN [BranchEntities] AS [b] ON [t].[OptionalReferenceBranchId] = [b].[Id] +SELECT [r].[Id], [r].[Name], [r].[OptionalRelatedId], [r].[RequiredRelatedId], [r0].[Id], [r0].[CollectionRootId], [r0].[Int], [r0].[Name], [r0].[OptionalNestedId], [r0].[RequiredNestedId], [r0].[String], [n].[Id], [n].[CollectionRelatedId], [n].[Int], [n].[Name], [n].[String] +FROM [RootEntity] AS [r] +INNER JOIN [RelatedType] AS [r0] ON [r].[RequiredRelatedId] = [r0].[Id] +LEFT JOIN [NestedType] AS [n] ON [r0].[Id] = [n].[CollectionRelatedId] +ORDER BY [r].[Id], [r0].[Id] """); } - public override async Task Include_branch_optional_collection(bool async) + public override async Task Include_nested_collection_on_optional(bool async) { - await base.Include_branch_optional_collection(async); + await base.Include_nested_collection_on_optional(async); AssertSql( """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId], [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId] -FROM [RootEntities] AS [r] -LEFT JOIN [TrunkEntities] AS [t] ON [r].[OptionalReferenceTrunkId] = [t].[Id] -LEFT JOIN [BranchEntities] AS [b] ON [t].[Id] = [b].[CollectionTrunkId] -ORDER BY [r].[Id], [t].[Id] +SELECT [r].[Id], [r].[Name], [r].[OptionalRelatedId], [r].[RequiredRelatedId], [r0].[Id], [r0].[CollectionRootId], [r0].[Int], [r0].[Name], [r0].[OptionalNestedId], [r0].[RequiredNestedId], [r0].[String], [n].[Id], [n].[CollectionRelatedId], [n].[Int], [n].[Name], [n].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RelatedType] AS [r0] ON [r].[OptionalRelatedId] = [r0].[Id] +LEFT JOIN [NestedType] AS [n] ON [r0].[Id] = [n].[CollectionRelatedId] +ORDER BY [r].[Id], [r0].[Id] """); } - public override async Task Include_branch_collection_collection(bool async) + public override async Task Include_nested_collection_on_collection(bool async) { - await base.Include_branch_collection_collection(async); + await base.Include_nested_collection_on_collection(async); AssertSql( """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [s].[Id], [s].[CollectionRootId], [s].[Name], [s].[OptionalReferenceBranchId], [s].[RequiredReferenceBranchId], [s].[Id0], [s].[CollectionTrunkId], [s].[Name0], [s].[OptionalReferenceLeafId], [s].[RequiredReferenceLeafId] -FROM [RootEntities] AS [r] +SELECT [r].[Id], [r].[Name], [r].[OptionalRelatedId], [r].[RequiredRelatedId], [s].[Id], [s].[CollectionRootId], [s].[Int], [s].[Name], [s].[OptionalNestedId], [s].[RequiredNestedId], [s].[String], [s].[Id0], [s].[CollectionRelatedId], [s].[Int0], [s].[Name0], [s].[String0] +FROM [RootEntity] AS [r] LEFT JOIN ( - SELECT [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId], [b].[Id] AS [Id0], [b].[CollectionTrunkId], [b].[Name] AS [Name0], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId] - FROM [TrunkEntities] AS [t] - LEFT JOIN [BranchEntities] AS [b] ON [t].[Id] = [b].[CollectionTrunkId] + SELECT [r0].[Id], [r0].[CollectionRootId], [r0].[Int], [r0].[Name], [r0].[OptionalNestedId], [r0].[RequiredNestedId], [r0].[String], [n].[Id] AS [Id0], [n].[CollectionRelatedId], [n].[Int] AS [Int0], [n].[Name] AS [Name0], [n].[String] AS [String0] + FROM [RelatedType] AS [r0] + LEFT JOIN [NestedType] AS [n] ON [r0].[Id] = [n].[CollectionRelatedId] ) AS [s] ON [r].[Id] = [s].[CollectionRootId] ORDER BY [r].[Id], [s].[Id] """); @@ -139,7 +132,4 @@ FROM [TrunkEntities] AS [t] [ConditionalFact] public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); - - private void AssertSql(params string[] expected) - => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/Navigations/NavigationsMiscellaneousSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/Navigations/NavigationsMiscellaneousSqlServerTest.cs new file mode 100644 index 00000000000..8073a182dc4 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/Navigations/NavigationsMiscellaneousSqlServerTest.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; + +public class NavigationsMiscellaneousSqlServerTest( + NavigationsSqlServerFixture fixture, + ITestOutputHelper testOutputHelper) + : NavigationsMiscellaneousRelationalTestBase(fixture, testOutputHelper) +{ + #region Simple filters + + public override async Task Where_related_property(bool async) + { + await base.Where_related_property(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelatedId], [r].[RequiredRelatedId] +FROM [RootEntity] AS [r] +INNER JOIN [RelatedType] AS [r0] ON [r].[RequiredRelatedId] = [r0].[Id] +WHERE [r0].[Int] = 8 +"""); + } + + public override async Task Where_optional_related_property(bool async) + { + await base.Where_optional_related_property(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelatedId], [r].[RequiredRelatedId] +FROM [RootEntity] AS [r] +LEFT JOIN [RelatedType] AS [r0] ON [r].[OptionalRelatedId] = [r0].[Id] +WHERE [r0].[Int] = 9 +"""); + } + + public override async Task Where_nested_related_property(bool async) + { + await base.Where_nested_related_property(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelatedId], [r].[RequiredRelatedId] +FROM [RootEntity] AS [r] +INNER JOIN [RelatedType] AS [r0] ON [r].[RequiredRelatedId] = [r0].[Id] +INNER JOIN [NestedType] AS [n] ON [r0].[RequiredNestedId] = [n].[Id] +WHERE [n].[Int] = 50 +"""); + } + + #endregion Simple filters + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/Navigations/NavigationsProjectionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/Navigations/NavigationsProjectionSqlServerTest.cs index ed6cc5371fb..05e74200997 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/Navigations/NavigationsProjectionSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/Navigations/NavigationsProjectionSqlServerTest.cs @@ -3,405 +3,279 @@ namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; -public class NavigationsProjectionSqlServerTest - : NavigationsProjectionRelationalTestBase +public class NavigationsProjectionSqlServerTest(NavigationsSqlServerFixture fixture, ITestOutputHelper testOutputHelper) + : NavigationsProjectionRelationalTestBase(fixture, testOutputHelper) { - public NavigationsProjectionSqlServerTest(NavigationsSqlServerFixture fixture, ITestOutputHelper testOutputHelper) - : base(fixture) - { - Fixture.TestSqlLoggerFactory.Clear(); - Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); - } - public override async Task Select_root(bool async, QueryTrackingBehavior queryTrackingBehavior) { await base.Select_root(async, queryTrackingBehavior); AssertSql( """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId] -FROM [RootEntities] AS [r] +SELECT [r].[Id], [r].[Name], [r].[OptionalRelatedId], [r].[RequiredRelatedId] +FROM [RootEntity] AS [r] """); } - public override async Task Select_trunk_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_trunk_optional(async, queryTrackingBehavior); + #region Simple properties - AssertSql( - """ -SELECT [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId] -FROM [RootEntities] AS [r] -LEFT JOIN [TrunkEntities] AS [t] ON [r].[OptionalReferenceTrunkId] = [t].[Id] -ORDER BY [r].[Id] -"""); - } - - public override async Task Select_trunk_required(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_related_property(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_trunk_required(async, queryTrackingBehavior); + await base.Select_related_property(async, queryTrackingBehavior); AssertSql( """ -SELECT [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] -ORDER BY [r].[Id] +SELECT [r0].[String] +FROM [RootEntity] AS [r] +INNER JOIN [RelatedType] AS [r0] ON [r].[RequiredRelatedId] = [r0].[Id] """); } - public override async Task Select_trunk_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_property(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_trunk_collection(async, queryTrackingBehavior); + await base.Select_optional_related_property(async, queryTrackingBehavior); AssertSql( """ -SELECT [r].[Id], [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId] -FROM [RootEntities] AS [r] -LEFT JOIN [TrunkEntities] AS [t] ON [r].[Id] = [t].[CollectionRootId] -ORDER BY [r].[Id] +SELECT [r0].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RelatedType] AS [r0] ON [r].[OptionalRelatedId] = [r0].[Id] """); } - public override async Task Select_branch_required_required(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_property_value_type(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_branch_required_required(async, queryTrackingBehavior); - - AssertSql( -""" -SELECT [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] -INNER JOIN [BranchEntities] AS [b] ON [t].[RequiredReferenceBranchId] = [b].[Id] -ORDER BY [r].[Id] -"""); - } - - public override async Task Select_branch_required_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_branch_required_optional(async, queryTrackingBehavior); + await base.Select_optional_related_property_value_type(async, queryTrackingBehavior); AssertSql( """ -SELECT [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] -LEFT JOIN [BranchEntities] AS [b] ON [t].[OptionalReferenceBranchId] = [b].[Id] -ORDER BY [r].[Id] +SELECT [r0].[Int] +FROM [RootEntity] AS [r] +LEFT JOIN [RelatedType] AS [r0] ON [r].[OptionalRelatedId] = [r0].[Id] """); } - public override async Task Select_branch_optional_required(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_branch_optional_required(async, queryTrackingBehavior); + #endregion Simple properties - AssertSql( - """ -SELECT [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] -INNER JOIN [BranchEntities] AS [b] ON [t].[RequiredReferenceBranchId] = [b].[Id] -ORDER BY [r].[Id] -"""); - } + #region Non-collection - public override async Task Select_branch_optional_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_related(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_branch_optional_optional(async, queryTrackingBehavior); + await base.Select_related(async, queryTrackingBehavior); AssertSql( """ -SELECT [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] -LEFT JOIN [BranchEntities] AS [b] ON [t].[OptionalReferenceBranchId] = [b].[Id] -ORDER BY [r].[Id] +SELECT [r0].[Id], [r0].[CollectionRootId], [r0].[Int], [r0].[Name], [r0].[OptionalNestedId], [r0].[RequiredNestedId], [r0].[String] +FROM [RootEntity] AS [r] +INNER JOIN [RelatedType] AS [r0] ON [r].[RequiredRelatedId] = [r0].[Id] """); } - public override async Task Select_branch_required_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_branch_required_collection(async, queryTrackingBehavior); + await base.Select_optional_related(async, queryTrackingBehavior); AssertSql( """ -SELECT [r].[Id], [t].[Id], [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] -LEFT JOIN [BranchEntities] AS [b] ON [t].[Id] = [b].[CollectionTrunkId] -ORDER BY [r].[Id], [t].[Id] +SELECT [r0].[Id], [r0].[CollectionRootId], [r0].[Int], [r0].[Name], [r0].[OptionalNestedId], [r0].[RequiredNestedId], [r0].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RelatedType] AS [r0] ON [r].[OptionalRelatedId] = [r0].[Id] """); } - public override async Task Select_branch_optional_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_required_related_required_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_branch_optional_collection(async, queryTrackingBehavior); + await base.Select_required_related_required_nested(async, queryTrackingBehavior); AssertSql( """ -SELECT [r].[Id], [t].[Id], [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] -LEFT JOIN [BranchEntities] AS [b] ON [t].[Id] = [b].[CollectionTrunkId] -ORDER BY [r].[Id], [t].[Id] +SELECT [n].[Id], [n].[CollectionRelatedId], [n].[Int], [n].[Name], [n].[String] +FROM [RootEntity] AS [r] +INNER JOIN [RelatedType] AS [r0] ON [r].[RequiredRelatedId] = [r0].[Id] +INNER JOIN [NestedType] AS [n] ON [r0].[RequiredNestedId] = [n].[Id] """); } - #region Multiple - - public override async Task Select_root_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_required_related_optional_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_root_duplicated(async, queryTrackingBehavior); + await base.Select_required_related_optional_nested(async, queryTrackingBehavior); AssertSql( """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId] -FROM [RootEntities] AS [r] +SELECT [n].[Id], [n].[CollectionRelatedId], [n].[Int], [n].[Name], [n].[String] +FROM [RootEntity] AS [r] +INNER JOIN [RelatedType] AS [r0] ON [r].[RequiredRelatedId] = [r0].[Id] +LEFT JOIN [NestedType] AS [n] ON [r0].[OptionalNestedId] = [n].[Id] """); } - public override async Task Select_trunk_and_branch_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_required_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_trunk_and_branch_duplicated(async, queryTrackingBehavior); + await base.Select_optional_related_required_nested(async, queryTrackingBehavior); AssertSql( """ -SELECT [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId], [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId] -FROM [RootEntities] AS [r] -LEFT JOIN [TrunkEntities] AS [t] ON [r].[OptionalReferenceTrunkId] = [t].[Id] -LEFT JOIN [BranchEntities] AS [b] ON [t].[RequiredReferenceBranchId] = [b].[Id] -ORDER BY [r].[Id] +SELECT [n].[Id], [n].[CollectionRelatedId], [n].[Int], [n].[Name], [n].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RelatedType] AS [r0] ON [r].[OptionalRelatedId] = [r0].[Id] +LEFT JOIN [NestedType] AS [n] ON [r0].[RequiredNestedId] = [n].[Id] """); } - public override async Task Select_trunk_and_trunk_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_optional_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_trunk_and_trunk_duplicated(async, queryTrackingBehavior); + await base.Select_optional_related_optional_nested(async, queryTrackingBehavior); AssertSql( """ -SELECT [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId], [l].[Id], [l].[CollectionBranchId], [l].[Name] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] -LEFT JOIN [BranchEntities] AS [b] ON [t].[OptionalReferenceBranchId] = [b].[Id] -LEFT JOIN [LeafEntities] AS [l] ON [b].[RequiredReferenceLeafId] = [l].[Id] -ORDER BY [r].[Id] +SELECT [n].[Id], [n].[CollectionRelatedId], [n].[Int], [n].[Name], [n].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RelatedType] AS [r0] ON [r].[OptionalRelatedId] = [r0].[Id] +LEFT JOIN [NestedType] AS [n] ON [r0].[OptionalNestedId] = [n].[Id] """); } - public override async Task Select_leaf_trunk_root(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_leaf_trunk_root(async, queryTrackingBehavior); + #endregion Non-collection - AssertSql( - """ -SELECT [l].[Id], [l].[CollectionBranchId], [l].[Name], [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId], [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] -INNER JOIN [BranchEntities] AS [b] ON [t].[RequiredReferenceBranchId] = [b].[Id] -INNER JOIN [LeafEntities] AS [l] ON [b].[RequiredReferenceLeafId] = [l].[Id] -"""); - } + #region Collection - public override async Task Select_multiple_branch_leaf(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_multiple_branch_leaf(async, queryTrackingBehavior); + await base.Select_related_collection(async, queryTrackingBehavior); AssertSql( """ -SELECT [r].[Id], [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId], [l].[Id], [l].[CollectionBranchId], [l].[Name], [t].[Id], [l0].[Id], [l0].[CollectionBranchId], [l0].[Name], [b0].[Id], [b0].[CollectionTrunkId], [b0].[Name], [b0].[OptionalReferenceLeafId], [b0].[RequiredReferenceLeafId] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] -INNER JOIN [BranchEntities] AS [b] ON [t].[RequiredReferenceBranchId] = [b].[Id] -LEFT JOIN [LeafEntities] AS [l] ON [b].[OptionalReferenceLeafId] = [l].[Id] -LEFT JOIN [LeafEntities] AS [l0] ON [b].[Id] = [l0].[CollectionBranchId] -LEFT JOIN [BranchEntities] AS [b0] ON [t].[Id] = [b0].[CollectionTrunkId] -ORDER BY [r].[Id], [t].[Id], [b].[Id], [l].[Id], [l0].[Id] -"""); - } - - #endregion Multiple - - #region Subquery - - public override async Task Select_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_subquery_root_set_required_trunk_FirstOrDefault_branch(async, queryTrackingBehavior); - - AssertSql( - """ -SELECT [s].[Id], [s].[CollectionTrunkId], [s].[Name], [s].[OptionalReferenceLeafId], [s].[RequiredReferenceLeafId] -FROM [RootEntities] AS [r] -OUTER APPLY ( - SELECT TOP(1) [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId] - FROM [RootEntities] AS [r0] - INNER JOIN [TrunkEntities] AS [t] ON [r0].[RequiredReferenceTrunkId] = [t].[Id] - INNER JOIN [BranchEntities] AS [b] ON [t].[RequiredReferenceBranchId] = [b].[Id] - ORDER BY [r0].[Id] -) AS [s] +SELECT [r].[Id], [r0].[Id], [r0].[CollectionRootId], [r0].[Int], [r0].[Name], [r0].[OptionalNestedId], [r0].[RequiredNestedId], [r0].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RelatedType] AS [r0] ON [r].[Id] = [r0].[CollectionRootId] ORDER BY [r].[Id] """); } - public override async Task Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(async, queryTrackingBehavior); + await base.Select_required_related_nested_collection(async, queryTrackingBehavior); AssertSql( """ -SELECT [s].[Id], [s].[CollectionTrunkId], [s].[Name], [s].[OptionalReferenceLeafId], [s].[RequiredReferenceLeafId] -FROM [RootEntities] AS [r] -OUTER APPLY ( - SELECT TOP(1) [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId] - FROM [RootEntities] AS [r0] - LEFT JOIN [TrunkEntities] AS [t] ON [r0].[OptionalReferenceTrunkId] = [t].[Id] - LEFT JOIN [BranchEntities] AS [b] ON [t].[OptionalReferenceBranchId] = [b].[Id] - ORDER BY [r0].[Id] -) AS [s] -ORDER BY [r].[Id] +SELECT [r].[Id], [r0].[Id], [n].[Id], [n].[CollectionRelatedId], [n].[Int], [n].[Name], [n].[String] +FROM [RootEntity] AS [r] +INNER JOIN [RelatedType] AS [r0] ON [r].[RequiredRelatedId] = [r0].[Id] +LEFT JOIN [NestedType] AS [n] ON [r0].[Id] = [n].[CollectionRelatedId] +ORDER BY [r].[Id], [r0].[Id] """); } - public override async Task Select_subquery_root_set_trunk_FirstOrDefault_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_subquery_root_set_trunk_FirstOrDefault_collection(async, queryTrackingBehavior); + await base.Select_optional_related_nested_collection(async, queryTrackingBehavior); AssertSql( """ -SELECT [r].[Id], [s].[Id], [s].[Id0], [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId], [s].[c] -FROM [RootEntities] AS [r] -OUTER APPLY ( - SELECT TOP(1) 1 AS [c], [r0].[Id], [t].[Id] AS [Id0] - FROM [RootEntities] AS [r0] - INNER JOIN [TrunkEntities] AS [t] ON [r0].[RequiredReferenceTrunkId] = [t].[Id] - ORDER BY [r0].[Id] -) AS [s] -LEFT JOIN [BranchEntities] AS [b] ON [s].[Id0] = [b].[CollectionTrunkId] -ORDER BY [r].[Id], [s].[Id], [s].[Id0] +SELECT [r].[Id], [r0].[Id], [n].[Id], [n].[CollectionRelatedId], [n].[Int], [n].[Name], [n].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RelatedType] AS [r0] ON [r].[OptionalRelatedId] = [r0].[Id] +LEFT JOIN [NestedType] AS [n] ON [r0].[Id] = [n].[CollectionRelatedId] +ORDER BY [r].[Id], [r0].[Id] """); } - public override async Task Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task SelectMany_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(async, queryTrackingBehavior); + await base.SelectMany_related_collection(async, queryTrackingBehavior); AssertSql( """ -SELECT [r].[Id], [t].[Id], [b].[Id], [s].[Id1], [s].[Id], [s].[Id0], [b1].[Id], [b1].[CollectionTrunkId], [b1].[Name], [b1].[OptionalReferenceLeafId], [b1].[RequiredReferenceLeafId], [s].[CollectionRootId], [s].[Name], [s].[OptionalReferenceBranchId], [s].[RequiredReferenceBranchId], [s].[CollectionTrunkId], [s].[Name0], [s].[OptionalReferenceLeafId], [s].[RequiredReferenceLeafId], [s].[Name1], [s].[c] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] -INNER JOIN [BranchEntities] AS [b] ON [t].[RequiredReferenceBranchId] = [b].[Id] -OUTER APPLY ( - SELECT TOP(1) [t0].[Id], [t0].[CollectionRootId], [t0].[Name], [t0].[OptionalReferenceBranchId], [t0].[RequiredReferenceBranchId], [b0].[Id] AS [Id0], [b0].[CollectionTrunkId], [b0].[Name] AS [Name0], [b0].[OptionalReferenceLeafId], [b0].[RequiredReferenceLeafId], [b].[Name] AS [Name1], 1 AS [c], [r0].[Id] AS [Id1] - FROM [RootEntities] AS [r0] - INNER JOIN [TrunkEntities] AS [t0] ON [r0].[RequiredReferenceTrunkId] = [t0].[Id] - INNER JOIN [BranchEntities] AS [b0] ON [t0].[RequiredReferenceBranchId] = [b0].[Id] - ORDER BY [r0].[Id] -) AS [s] -LEFT JOIN [BranchEntities] AS [b1] ON [t].[Id] = [b1].[CollectionTrunkId] -ORDER BY [r].[Id], [t].[Id], [b].[Id], [s].[Id1], [s].[Id], [s].[Id0] +SELECT [r0].[Id], [r0].[CollectionRootId], [r0].[Int], [r0].[Name], [r0].[OptionalNestedId], [r0].[RequiredNestedId], [r0].[String] +FROM [RootEntity] AS [r] +INNER JOIN [RelatedType] AS [r0] ON [r].[Id] = [r0].[CollectionRootId] """); } - public override async Task Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task SelectMany_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(async, queryTrackingBehavior); + await base.SelectMany_required_related_nested_collection(async, queryTrackingBehavior); AssertSql( """ -SELECT [r].[Id], [t].[Id], [r1].[Id], [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId], [r1].[c] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] -OUTER APPLY ( - SELECT TOP(1) 1 AS [c], [r0].[Id] - FROM [RootEntities] AS [r0] - ORDER BY [r0].[Id] -) AS [r1] -LEFT JOIN [BranchEntities] AS [b] ON [t].[Id] = [b].[CollectionTrunkId] -ORDER BY [r].[Id], [t].[Id], [r1].[Id] +SELECT [n].[Id], [n].[CollectionRelatedId], [n].[Int], [n].[Name], [n].[String] +FROM [RootEntity] AS [r] +INNER JOIN [RelatedType] AS [r0] ON [r].[RequiredRelatedId] = [r0].[Id] +INNER JOIN [NestedType] AS [n] ON [r0].[Id] = [n].[CollectionRelatedId] """); } - #endregion Subquery - - #region SelectMany - - public override async Task SelectMany_trunk_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task SelectMany_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.SelectMany_trunk_collection(async, queryTrackingBehavior); + await base.SelectMany_optional_related_nested_collection(async, queryTrackingBehavior); AssertSql( """ -SELECT [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[Id] = [t].[CollectionRootId] +SELECT [n].[Id], [n].[CollectionRelatedId], [n].[Int], [n].[Name], [n].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RelatedType] AS [r0] ON [r].[OptionalRelatedId] = [r0].[Id] +INNER JOIN [NestedType] AS [n] ON [r0].[Id] = [n].[CollectionRelatedId] """); } - public override async Task SelectMany_required_trunk_reference_branch_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.SelectMany_required_trunk_reference_branch_collection(async, queryTrackingBehavior); + #endregion Collection - AssertSql( - """ -SELECT [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[RequiredReferenceTrunkId] = [t].[Id] -INNER JOIN [BranchEntities] AS [b] ON [t].[Id] = [b].[CollectionTrunkId] -"""); - } + #region Multiple - public override async Task SelectMany_optional_trunk_reference_branch_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_root_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.SelectMany_optional_trunk_reference_branch_collection(async, queryTrackingBehavior); + await base.Select_root_duplicated(async, queryTrackingBehavior); AssertSql( """ -SELECT [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId] -FROM [RootEntities] AS [r] -LEFT JOIN [TrunkEntities] AS [t] ON [r].[OptionalReferenceTrunkId] = [t].[Id] -INNER JOIN [BranchEntities] AS [b] ON [t].[Id] = [b].[CollectionTrunkId] +SELECT [r].[Id], [r].[Name], [r].[OptionalRelatedId], [r].[RequiredRelatedId] +FROM [RootEntity] AS [r] """); } - #endregion SelectMany + #endregion Multiple - #region NavigationProjectionTestBase + #region Subquery - public override async Task Select_everything(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_subquery_required_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_everything(async, queryTrackingBehavior); + await base.Select_subquery_required_related_FirstOrDefault(async, queryTrackingBehavior); AssertSql( """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId], [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId], [l].[Id], [l].[CollectionBranchId], [l].[Name] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[Id] = [t].[Id] -INNER JOIN [BranchEntities] AS [b] ON [t].[Id] = [b].[Id] -INNER JOIN [LeafEntities] AS [l] ON [b].[Id] = [l].[Id] +SELECT [s].[Id], [s].[CollectionRelatedId], [s].[Int], [s].[Name], [s].[String] +FROM [RootEntity] AS [r] +OUTER APPLY ( + SELECT TOP(1) [n].[Id], [n].[CollectionRelatedId], [n].[Int], [n].[Name], [n].[String] + FROM [RootEntity] AS [r0] + INNER JOIN [RelatedType] AS [r1] ON [r0].[RequiredRelatedId] = [r1].[Id] + INNER JOIN [NestedType] AS [n] ON [r1].[RequiredNestedId] = [n].[Id] + ORDER BY [r0].[Id] +) AS [s] """); } - public override async Task Select_everything_using_joins(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_subquery_optional_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_everything_using_joins(async, queryTrackingBehavior); + await base.Select_subquery_optional_related_FirstOrDefault(async, queryTrackingBehavior); AssertSql( """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [t].[Id], [t].[CollectionRootId], [t].[Name], [t].[OptionalReferenceBranchId], [t].[RequiredReferenceBranchId], [b].[Id], [b].[CollectionTrunkId], [b].[Name], [b].[OptionalReferenceLeafId], [b].[RequiredReferenceLeafId], [l].[Id], [l].[CollectionBranchId], [l].[Name] -FROM [RootEntities] AS [r] -INNER JOIN [TrunkEntities] AS [t] ON [r].[Id] = [t].[Id] -INNER JOIN [BranchEntities] AS [b] ON [t].[Id] = [b].[Id] -INNER JOIN [LeafEntities] AS [l] ON [b].[Id] = [l].[Id] +SELECT [s].[Id], [s].[CollectionRelatedId], [s].[Int], [s].[Name], [s].[String] +FROM [RootEntity] AS [r] +OUTER APPLY ( + SELECT TOP(1) [n].[Id], [n].[CollectionRelatedId], [n].[Int], [n].[Name], [n].[String] + FROM [RootEntity] AS [r0] + LEFT JOIN [RelatedType] AS [r1] ON [r0].[OptionalRelatedId] = [r1].[Id] + LEFT JOIN [NestedType] AS [n] ON [r1].[RequiredNestedId] = [n].[Id] + ORDER BY [r0].[Id] +) AS [s] """); } - #endregion NavigationProjectionTestBase + #endregion Subquery [ConditionalFact] public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); - - private void AssertSql(params string[] expected) - => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/Navigations/NavigationsSqlServerFixture.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/Navigations/NavigationsSqlServerFixture.cs index 583f61a9d51..3cf039aa856 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/Navigations/NavigationsSqlServerFixture.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/Navigations/NavigationsSqlServerFixture.cs @@ -3,11 +3,8 @@ namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; -public class NavigationsSqlServerFixture : NavigationsRelationalFixtureBase, ITestSqlLoggerFactory +public class NavigationsSqlServerFixture : NavigationsRelationalFixtureBase { protected override ITestStoreFactory TestStoreFactory => SqlServerTestStoreFactory.Instance; - - public TestSqlLoggerFactory TestSqlLoggerFactory - => (TestSqlLoggerFactory)ListLoggerFactory; } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonCollectionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonCollectionSqlServerTest.cs new file mode 100644 index 00000000000..3027dda6ed9 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonCollectionSqlServerTest.cs @@ -0,0 +1,117 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedJson; + +public class OwnedJsonCollectionSqlServerTest(OwnedJsonSqlServerFixture fixture, ITestOutputHelper testOutputHelper) + : OwnedJsonCollectionRelationalTestBase(fixture, testOutputHelper) +{ + public override async Task Count(bool async) + { + await base.Count(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE ( + SELECT COUNT(*) + FROM OPENJSON([r].[RelatedCollection], '$') AS [r0]) = 2 +"""); + } + + public override async Task Where(bool async) + { + await base.Where(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE ( + SELECT COUNT(*) + FROM OPENJSON([r].[RelatedCollection], '$') WITH ( + [Id] int '$.Id', + [Int] int '$.Int', + [Name] nvarchar(max) '$.Name', + [String] nvarchar(max) '$.String' + ) AS [r0] + WHERE [r0].[Int] <> 50) = 2 +"""); + } + + public override async Task Index_constant(bool async) + { + await base.Index_constant(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE CAST(JSON_VALUE([r].[RelatedCollection], '$[0].Int') AS int) = 21 +"""); + } + + public override async Task OrderBy_ElementAt(bool async) + { + await base.OrderBy_ElementAt(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE ( + SELECT [r0].[Int] + FROM OPENJSON([r].[RelatedCollection], '$') WITH ( + [Id] int '$.Id', + [Int] int '$.Int', + [Name] nvarchar(max) '$.Name', + [String] nvarchar(max) '$.String' + ) AS [r0] + ORDER BY [r0].[Id] + OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) = 21 +"""); + } + + public override async Task Index_parameter(bool async) + { + await base.Index_parameter(async); + + AssertSql( + """ +@i='?' (DbType = Int32) + +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE CAST(JSON_VALUE([r].[RelatedCollection], '$[' + CAST(@i AS nvarchar(max)) + '].Int') AS int) = 21 +"""); + } + + public override async Task Index_column(bool async) + { + await base.Index_column(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE CAST(JSON_VALUE([r].[RelatedCollection], '$[' + CAST([r].[Id] - 1 AS nvarchar(max)) + '].Int') AS int) = 21 +"""); + } + + public override async Task Index_out_of_bounds(bool async) + { + await base.Index_out_of_bounds(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE CAST(JSON_VALUE([r].[RelatedCollection], '$[9999].Int') AS int) = 50 +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonMiscellaneousSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonMiscellaneousSqlServerTest.cs new file mode 100644 index 00000000000..29b9b51349b --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonMiscellaneousSqlServerTest.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedJson; + +public class OwnedJsonMiscellaneousSqlServerTest( + OwnedJsonSqlServerFixture fixture, + ITestOutputHelper testOutputHelper) + : OwnedJsonMiscellaneousRelationalTestBase(fixture, testOutputHelper) +{ + #region Simple filters + + public override async Task Where_related_property(bool async) + { + await base.Where_related_property(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE CAST(JSON_VALUE([r].[RequiredRelated], '$.Int') AS int) = 8 +"""); + } + + public override async Task Where_optional_related_property(bool async) + { + await base.Where_optional_related_property(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE CAST(JSON_VALUE([r].[OptionalRelated], '$.Int') AS int) = 9 +"""); + } + + public override async Task Where_nested_related_property(bool async) + { + await base.Where_nested_related_property(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +WHERE CAST(JSON_VALUE([r].[RequiredRelated], '$.RequiredNested.Int') AS int) = 50 +"""); + } + + #endregion Simple filters + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonProjectionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonProjectionSqlServerTest.cs new file mode 100644 index 00000000000..5d40d7aa531 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonProjectionSqlServerTest.cs @@ -0,0 +1,315 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedJson; + +public class OwnedJsonProjectionSqlServerTest(OwnedJsonSqlServerFixture fixture, ITestOutputHelper testOutputHelper) + : OwnedJsonProjectionRelationalTestBase(fixture, testOutputHelper) +{ + public override async Task Select_root(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_root(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +"""); + } + + #region Simple properties + + public override async Task Select_related_property(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_related_property(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT JSON_VALUE([r].[RequiredRelated], '$.String') +FROM [RootEntity] AS [r] +"""); + } + + public override async Task Select_optional_related_property(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_optional_related_property(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT JSON_VALUE([r].[OptionalRelated], '$.String') +FROM [RootEntity] AS [r] +"""); + } + + public override async Task Select_optional_related_property_value_type(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_optional_related_property_value_type(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT CAST(JSON_VALUE([r].[OptionalRelated], '$.Int') AS int) +FROM [RootEntity] AS [r] +"""); + } + + #endregion Simple properties + + #region Non-collection + + public override async Task Select_related(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_related(async, queryTrackingBehavior); + + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + { + AssertSql( + """ +SELECT [r].[RequiredRelated], [r].[Id] +FROM [RootEntity] AS [r] +"""); + } + } + + public override async Task Select_optional_related(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_optional_related(async, queryTrackingBehavior); + + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + { + AssertSql( + """ +SELECT [r].[OptionalRelated], [r].[Id] +FROM [RootEntity] AS [r] +"""); + } + } + + public override async Task Select_required_related_required_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_required_related_required_nested(async, queryTrackingBehavior); + + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + { + AssertSql( + """ +SELECT JSON_QUERY([r].[RequiredRelated], '$.RequiredNested'), [r].[Id] +FROM [RootEntity] AS [r] +"""); + } + } + + public override async Task Select_required_related_optional_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_required_related_optional_nested(async, queryTrackingBehavior); + + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + { + AssertSql( + """ +SELECT JSON_QUERY([r].[RequiredRelated], '$.OptionalNested'), [r].[Id] +FROM [RootEntity] AS [r] +"""); + } + } + + public override async Task Select_optional_related_required_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_optional_related_required_nested(async, queryTrackingBehavior); + + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + { + AssertSql( + """ +SELECT JSON_QUERY([r].[OptionalRelated], '$.RequiredNested'), [r].[Id] +FROM [RootEntity] AS [r] +"""); + } + } + + public override async Task Select_optional_related_optional_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_optional_related_optional_nested(async, queryTrackingBehavior); + + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + { + AssertSql( + """ +SELECT JSON_QUERY([r].[OptionalRelated], '$.OptionalNested'), [r].[Id] +FROM [RootEntity] AS [r] +"""); + } + } + + #endregion Non-collection + + #region Collection + + public override async Task Select_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_related_collection(async, queryTrackingBehavior); + + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + { + AssertSql( + """ +SELECT [r].[RelatedCollection], [r].[Id] +FROM [RootEntity] AS [r] +ORDER BY [r].[Id] +"""); + } + } + + public override async Task Select_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_required_related_nested_collection(async, queryTrackingBehavior); + + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + { + AssertSql( + """ +SELECT JSON_QUERY([r].[RequiredRelated], '$.NestedCollection'), [r].[Id] +FROM [RootEntity] AS [r] +"""); + } + } + + public override async Task Select_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_optional_related_nested_collection(async, queryTrackingBehavior); + + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + { + AssertSql( + """ +SELECT JSON_QUERY([r].[OptionalRelated], '$.NestedCollection'), [r].[Id] +FROM [RootEntity] AS [r] +"""); + } + } + + public override async Task SelectMany_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.SelectMany_related_collection(async, queryTrackingBehavior); + + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + { + AssertSql( + """ +SELECT [r].[Id], [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String], [r0].[NestedCollection], [r0].[OptionalNested], [r0].[RequiredNested] +FROM [RootEntity] AS [r] +CROSS APPLY OPENJSON([r].[RelatedCollection], '$') WITH ( + [Id] int '$.Id', + [Int] int '$.Int', + [Name] nvarchar(max) '$.Name', + [String] nvarchar(max) '$.String', + [NestedCollection] nvarchar(max) '$.NestedCollection' AS JSON, + [OptionalNested] nvarchar(max) '$.OptionalNested' AS JSON, + [RequiredNested] nvarchar(max) '$.RequiredNested' AS JSON +) AS [r0] +"""); + } + } + + public override async Task SelectMany_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.SelectMany_required_related_nested_collection(async, queryTrackingBehavior); + + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + { + AssertSql( + """ +SELECT [r].[Id], [n].[Id], [n].[Int], [n].[Name], [n].[String] +FROM [RootEntity] AS [r] +CROSS APPLY OPENJSON([r].[RequiredRelated], '$.NestedCollection') WITH ( + [Id] int '$.Id', + [Int] int '$.Int', + [Name] nvarchar(max) '$.Name', + [String] nvarchar(max) '$.String' +) AS [n] +"""); + } + } + + public override async Task SelectMany_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.SelectMany_optional_related_nested_collection(async, queryTrackingBehavior); + + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + { + AssertSql( + """ +SELECT [r].[Id], [n].[Id], [n].[Int], [n].[Name], [n].[String] +FROM [RootEntity] AS [r] +CROSS APPLY OPENJSON([r].[OptionalRelated], '$.NestedCollection') WITH ( + [Id] int '$.Id', + [Int] int '$.Int', + [Name] nvarchar(max) '$.Name', + [String] nvarchar(max) '$.String' +) AS [n] +"""); + } + } + + #endregion Collection + + #region Multiple + + public override async Task Select_root_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_root_duplicated(async, queryTrackingBehavior); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated], [r].[OptionalRelated], [r].[RelatedCollection], [r].[RequiredRelated] +FROM [RootEntity] AS [r] +"""); + } + + #endregion Multiple + + #region Subquery + + public override async Task Select_subquery_required_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_subquery_required_related_FirstOrDefault(async, queryTrackingBehavior); + + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + { + AssertSql( + """ +SELECT [r1].[c], [r1].[Id] +FROM [RootEntity] AS [r] +OUTER APPLY ( + SELECT TOP(1) JSON_QUERY([r0].[RequiredRelated], '$.RequiredNested') AS [c], [r0].[Id] + FROM [RootEntity] AS [r0] + ORDER BY [r0].[Id] +) AS [r1] +"""); + } + } + + public override async Task Select_subquery_optional_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + { + await base.Select_subquery_optional_related_FirstOrDefault(async, queryTrackingBehavior); + + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) + { + AssertSql( + """ +SELECT [r1].[c], [r1].[Id] +FROM [RootEntity] AS [r] +OUTER APPLY ( + SELECT TOP(1) JSON_QUERY([r0].[OptionalRelated], '$.RequiredNested') AS [c], [r0].[Id] + FROM [RootEntity] AS [r0] + ORDER BY [r0].[Id] +) AS [r1] +"""); + } + } + + #endregion Subquery + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonSqlServerFixture.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonSqlServerFixture.cs new file mode 100644 index 00000000000..5b843ce5961 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonSqlServerFixture.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedJson; + +public class OwnedJsonSqlServerFixture : OwnedJsonRelationalFixtureBase +{ + protected override ITestStoreFactory TestStoreFactory + => SqlServerTestStoreFactory.Instance; +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonTypeSqlServerFixture.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonTypeSqlServerFixture.cs new file mode 100644 index 00000000000..03c457cc9ce --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonTypeSqlServerFixture.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedJson; + +public class OwnedJsonTypeSqlServerFixture : OwnedJsonRelationalFixtureBase +{ + protected override string StoreName => "OwnedJsonTypeRelationshipsQueryTest"; + + protected override ITestStoreFactory TestStoreFactory + => SqlServerTestStoreFactory.Instance; + + // protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) + // { + // base.OnModelCreating(modelBuilder, context); + + // modelBuilder.Entity().OwnsOne(x => x.RequiredTrunk).HasColumnType("json"); + // modelBuilder.Entity().OwnsOne(x => x.OptionalTrunk).HasColumnType("json"); + // modelBuilder.Entity().OwnsMany(x => x.CollectionTrunk).HasColumnType("json"); + // } + + public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) + => base.AddOptions(builder.ConfigureWarnings(b => b.Ignore(SqlServerEventId.JsonTypeExperimental))); +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsCollectionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsCollectionSqlServerTest.cs new file mode 100644 index 00000000000..945dfaa9abf --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsCollectionSqlServerTest.cs @@ -0,0 +1,241 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; + +public class OwnedNavigationsCollectionSqlServerTest(OwnedNavigationsSqlServerFixture fixture, ITestOutputHelper testOutputHelper) + : OwnedNavigationsCollectionRelationalTestBase(fixture, testOutputHelper) +{ + public override async Task Count(bool async) + { + await base.Count(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [o].[RootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r1].[RootEntityId], [r2].[RelatedTypeRootEntityId], [r3].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [o2].[Int], [o2].[Name], [o2].[String], [o0].[Id], [o0].[Int], [o0].[Name], [o0].[String], [o1].[Id], [o1].[Int], [o1].[Name], [o1].[String], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [s].[Int0], [s].[Name0], [s].[String0], [s].[Id1], [s].[Int1], [s].[Name1], [s].[String1], [s].[Id2], [s].[Int2], [s].[Name2], [s].[String2], [r1].[Id], [r1].[Int], [r1].[Name], [r1].[String], [r8].[RelatedTypeRootEntityId], [r8].[Id], [r8].[Int], [r8].[Name], [r8].[String], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String], [r3].[Id], [r3].[Int], [r3].[Name], [r3].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated] AS [o] ON [r].[Id] = [o].[RootEntityId] +LEFT JOIN [OptionalRelated_OptionalNested] AS [o0] ON [o].[RootEntityId] = [o0].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_RequiredNested] AS [o1] ON [o].[RootEntityId] = [o1].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated] AS [r1] ON [r].[Id] = [r1].[RootEntityId] +LEFT JOIN [RequiredRelated_OptionalNested] AS [r2] ON [r1].[RootEntityId] = [r2].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated_RequiredNested] AS [r3] ON [r1].[RootEntityId] = [r3].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o2] ON [o].[RootEntityId] = [o2].[RelatedTypeRootEntityId] +LEFT JOIN ( + SELECT [r4].[RootEntityId], [r4].[Id], [r4].[Int], [r4].[Name], [r4].[String], [r5].[RelatedTypeRootEntityId], [r5].[RelatedTypeId], [r6].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId0], [r6].[RelatedTypeId] AS [RelatedTypeId0], [r7].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId1], [r7].[RelatedTypeId] AS [RelatedTypeId1], [r7].[Id] AS [Id0], [r7].[Int] AS [Int0], [r7].[Name] AS [Name0], [r7].[String] AS [String0], [r5].[Id] AS [Id1], [r5].[Int] AS [Int1], [r5].[Name] AS [Name1], [r5].[String] AS [String1], [r6].[Id] AS [Id2], [r6].[Int] AS [Int2], [r6].[Name] AS [Name2], [r6].[String] AS [String2] + FROM [RelatedCollection] AS [r4] + LEFT JOIN [RelatedCollection_OptionalNested] AS [r5] ON [r4].[RootEntityId] = [r5].[RelatedTypeRootEntityId] AND [r4].[Id] = [r5].[RelatedTypeId] + LEFT JOIN [RelatedCollection_RequiredNested] AS [r6] ON [r4].[RootEntityId] = [r6].[RelatedTypeRootEntityId] AND [r4].[Id] = [r6].[RelatedTypeId] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r7] ON [r4].[RootEntityId] = [r7].[RelatedTypeRootEntityId] AND [r4].[Id] = [r7].[RelatedTypeId] +) AS [s] ON [r].[Id] = [s].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r8] ON [r1].[RootEntityId] = [r8].[RelatedTypeRootEntityId] +WHERE ( + SELECT COUNT(*) + FROM [RelatedCollection] AS [r0] + WHERE [r].[Id] = [r0].[RootEntityId]) = 2 +ORDER BY [r].[Id], [o].[RootEntityId], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r1].[RootEntityId], [r2].[RelatedTypeRootEntityId], [r3].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [s].[RootEntityId], [s].[Id], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [r8].[RelatedTypeRootEntityId] +"""); + } + + public override async Task Where(bool async) + { + await base.Where(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [o].[RootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r1].[RootEntityId], [r2].[RelatedTypeRootEntityId], [r3].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [o2].[Int], [o2].[Name], [o2].[String], [o0].[Id], [o0].[Int], [o0].[Name], [o0].[String], [o1].[Id], [o1].[Int], [o1].[Name], [o1].[String], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [s].[Int0], [s].[Name0], [s].[String0], [s].[Id1], [s].[Int1], [s].[Name1], [s].[String1], [s].[Id2], [s].[Int2], [s].[Name2], [s].[String2], [r1].[Id], [r1].[Int], [r1].[Name], [r1].[String], [r8].[RelatedTypeRootEntityId], [r8].[Id], [r8].[Int], [r8].[Name], [r8].[String], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String], [r3].[Id], [r3].[Int], [r3].[Name], [r3].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated] AS [o] ON [r].[Id] = [o].[RootEntityId] +LEFT JOIN [OptionalRelated_OptionalNested] AS [o0] ON [o].[RootEntityId] = [o0].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_RequiredNested] AS [o1] ON [o].[RootEntityId] = [o1].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated] AS [r1] ON [r].[Id] = [r1].[RootEntityId] +LEFT JOIN [RequiredRelated_OptionalNested] AS [r2] ON [r1].[RootEntityId] = [r2].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated_RequiredNested] AS [r3] ON [r1].[RootEntityId] = [r3].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o2] ON [o].[RootEntityId] = [o2].[RelatedTypeRootEntityId] +LEFT JOIN ( + SELECT [r4].[RootEntityId], [r4].[Id], [r4].[Int], [r4].[Name], [r4].[String], [r5].[RelatedTypeRootEntityId], [r5].[RelatedTypeId], [r6].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId0], [r6].[RelatedTypeId] AS [RelatedTypeId0], [r7].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId1], [r7].[RelatedTypeId] AS [RelatedTypeId1], [r7].[Id] AS [Id0], [r7].[Int] AS [Int0], [r7].[Name] AS [Name0], [r7].[String] AS [String0], [r5].[Id] AS [Id1], [r5].[Int] AS [Int1], [r5].[Name] AS [Name1], [r5].[String] AS [String1], [r6].[Id] AS [Id2], [r6].[Int] AS [Int2], [r6].[Name] AS [Name2], [r6].[String] AS [String2] + FROM [RelatedCollection] AS [r4] + LEFT JOIN [RelatedCollection_OptionalNested] AS [r5] ON [r4].[RootEntityId] = [r5].[RelatedTypeRootEntityId] AND [r4].[Id] = [r5].[RelatedTypeId] + LEFT JOIN [RelatedCollection_RequiredNested] AS [r6] ON [r4].[RootEntityId] = [r6].[RelatedTypeRootEntityId] AND [r4].[Id] = [r6].[RelatedTypeId] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r7] ON [r4].[RootEntityId] = [r7].[RelatedTypeRootEntityId] AND [r4].[Id] = [r7].[RelatedTypeId] +) AS [s] ON [r].[Id] = [s].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r8] ON [r1].[RootEntityId] = [r8].[RelatedTypeRootEntityId] +WHERE ( + SELECT COUNT(*) + FROM [RelatedCollection] AS [r0] + WHERE [r].[Id] = [r0].[RootEntityId] AND [r0].[Int] <> 50) = 2 +ORDER BY [r].[Id], [o].[RootEntityId], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r1].[RootEntityId], [r2].[RelatedTypeRootEntityId], [r3].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [s].[RootEntityId], [s].[Id], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [r8].[RelatedTypeRootEntityId] +"""); + } + + public override async Task OrderBy_ElementAt(bool async) + { + await base.OrderBy_ElementAt(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [o].[RootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r1].[RootEntityId], [r2].[RelatedTypeRootEntityId], [r3].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [o2].[Int], [o2].[Name], [o2].[String], [o0].[Id], [o0].[Int], [o0].[Name], [o0].[String], [o1].[Id], [o1].[Int], [o1].[Name], [o1].[String], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [s].[Int0], [s].[Name0], [s].[String0], [s].[Id1], [s].[Int1], [s].[Name1], [s].[String1], [s].[Id2], [s].[Int2], [s].[Name2], [s].[String2], [r1].[Id], [r1].[Int], [r1].[Name], [r1].[String], [r8].[RelatedTypeRootEntityId], [r8].[Id], [r8].[Int], [r8].[Name], [r8].[String], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String], [r3].[Id], [r3].[Int], [r3].[Name], [r3].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated] AS [o] ON [r].[Id] = [o].[RootEntityId] +LEFT JOIN [OptionalRelated_OptionalNested] AS [o0] ON [o].[RootEntityId] = [o0].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_RequiredNested] AS [o1] ON [o].[RootEntityId] = [o1].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated] AS [r1] ON [r].[Id] = [r1].[RootEntityId] +LEFT JOIN [RequiredRelated_OptionalNested] AS [r2] ON [r1].[RootEntityId] = [r2].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated_RequiredNested] AS [r3] ON [r1].[RootEntityId] = [r3].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o2] ON [o].[RootEntityId] = [o2].[RelatedTypeRootEntityId] +LEFT JOIN ( + SELECT [r4].[RootEntityId], [r4].[Id], [r4].[Int], [r4].[Name], [r4].[String], [r5].[RelatedTypeRootEntityId], [r5].[RelatedTypeId], [r6].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId0], [r6].[RelatedTypeId] AS [RelatedTypeId0], [r7].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId1], [r7].[RelatedTypeId] AS [RelatedTypeId1], [r7].[Id] AS [Id0], [r7].[Int] AS [Int0], [r7].[Name] AS [Name0], [r7].[String] AS [String0], [r5].[Id] AS [Id1], [r5].[Int] AS [Int1], [r5].[Name] AS [Name1], [r5].[String] AS [String1], [r6].[Id] AS [Id2], [r6].[Int] AS [Int2], [r6].[Name] AS [Name2], [r6].[String] AS [String2] + FROM [RelatedCollection] AS [r4] + LEFT JOIN [RelatedCollection_OptionalNested] AS [r5] ON [r4].[RootEntityId] = [r5].[RelatedTypeRootEntityId] AND [r4].[Id] = [r5].[RelatedTypeId] + LEFT JOIN [RelatedCollection_RequiredNested] AS [r6] ON [r4].[RootEntityId] = [r6].[RelatedTypeRootEntityId] AND [r4].[Id] = [r6].[RelatedTypeId] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r7] ON [r4].[RootEntityId] = [r7].[RelatedTypeRootEntityId] AND [r4].[Id] = [r7].[RelatedTypeId] +) AS [s] ON [r].[Id] = [s].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r8] ON [r1].[RootEntityId] = [r8].[RelatedTypeRootEntityId] +WHERE ( + SELECT [r0].[Int] + FROM [RelatedCollection] AS [r0] + WHERE [r].[Id] = [r0].[RootEntityId] + ORDER BY [r0].[Id] + OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) = 21 +ORDER BY [r].[Id], [o].[RootEntityId], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r1].[RootEntityId], [r2].[RelatedTypeRootEntityId], [r3].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [s].[RootEntityId], [s].[Id], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [r8].[RelatedTypeRootEntityId] +"""); + } + + public override async Task Index_constant(bool async) + { + await base.Index_constant(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [o].[RootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r1].[RootEntityId], [r2].[RelatedTypeRootEntityId], [r3].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [o2].[Int], [o2].[Name], [o2].[String], [o0].[Id], [o0].[Int], [o0].[Name], [o0].[String], [o1].[Id], [o1].[Int], [o1].[Name], [o1].[String], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [s].[Int0], [s].[Name0], [s].[String0], [s].[Id1], [s].[Int1], [s].[Name1], [s].[String1], [s].[Id2], [s].[Int2], [s].[Name2], [s].[String2], [r1].[Id], [r1].[Int], [r1].[Name], [r1].[String], [r8].[RelatedTypeRootEntityId], [r8].[Id], [r8].[Int], [r8].[Name], [r8].[String], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String], [r3].[Id], [r3].[Int], [r3].[Name], [r3].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated] AS [o] ON [r].[Id] = [o].[RootEntityId] +LEFT JOIN [OptionalRelated_OptionalNested] AS [o0] ON [o].[RootEntityId] = [o0].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_RequiredNested] AS [o1] ON [o].[RootEntityId] = [o1].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated] AS [r1] ON [r].[Id] = [r1].[RootEntityId] +LEFT JOIN [RequiredRelated_OptionalNested] AS [r2] ON [r1].[RootEntityId] = [r2].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated_RequiredNested] AS [r3] ON [r1].[RootEntityId] = [r3].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o2] ON [o].[RootEntityId] = [o2].[RelatedTypeRootEntityId] +LEFT JOIN ( + SELECT [r4].[RootEntityId], [r4].[Id], [r4].[Int], [r4].[Name], [r4].[String], [r5].[RelatedTypeRootEntityId], [r5].[RelatedTypeId], [r6].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId0], [r6].[RelatedTypeId] AS [RelatedTypeId0], [r7].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId1], [r7].[RelatedTypeId] AS [RelatedTypeId1], [r7].[Id] AS [Id0], [r7].[Int] AS [Int0], [r7].[Name] AS [Name0], [r7].[String] AS [String0], [r5].[Id] AS [Id1], [r5].[Int] AS [Int1], [r5].[Name] AS [Name1], [r5].[String] AS [String1], [r6].[Id] AS [Id2], [r6].[Int] AS [Int2], [r6].[Name] AS [Name2], [r6].[String] AS [String2] + FROM [RelatedCollection] AS [r4] + LEFT JOIN [RelatedCollection_OptionalNested] AS [r5] ON [r4].[RootEntityId] = [r5].[RelatedTypeRootEntityId] AND [r4].[Id] = [r5].[RelatedTypeId] + LEFT JOIN [RelatedCollection_RequiredNested] AS [r6] ON [r4].[RootEntityId] = [r6].[RelatedTypeRootEntityId] AND [r4].[Id] = [r6].[RelatedTypeId] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r7] ON [r4].[RootEntityId] = [r7].[RelatedTypeRootEntityId] AND [r4].[Id] = [r7].[RelatedTypeId] +) AS [s] ON [r].[Id] = [s].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r8] ON [r1].[RootEntityId] = [r8].[RelatedTypeRootEntityId] +WHERE ( + SELECT [r0].[Int] + FROM [RelatedCollection] AS [r0] + WHERE [r].[Id] = [r0].[RootEntityId] + ORDER BY (SELECT 1) + OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) = 21 +ORDER BY [r].[Id], [o].[RootEntityId], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r1].[RootEntityId], [r2].[RelatedTypeRootEntityId], [r3].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [s].[RootEntityId], [s].[Id], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [r8].[RelatedTypeRootEntityId] +"""); + } + + public override async Task Index_parameter(bool async) + { + await base.Index_parameter(async); + + AssertSql( + """ +@i='?' (DbType = Int32) + +SELECT [r].[Id], [r].[Name], [o].[RootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r1].[RootEntityId], [r2].[RelatedTypeRootEntityId], [r3].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [o2].[Int], [o2].[Name], [o2].[String], [o0].[Id], [o0].[Int], [o0].[Name], [o0].[String], [o1].[Id], [o1].[Int], [o1].[Name], [o1].[String], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [s].[Int0], [s].[Name0], [s].[String0], [s].[Id1], [s].[Int1], [s].[Name1], [s].[String1], [s].[Id2], [s].[Int2], [s].[Name2], [s].[String2], [r1].[Id], [r1].[Int], [r1].[Name], [r1].[String], [r8].[RelatedTypeRootEntityId], [r8].[Id], [r8].[Int], [r8].[Name], [r8].[String], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String], [r3].[Id], [r3].[Int], [r3].[Name], [r3].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated] AS [o] ON [r].[Id] = [o].[RootEntityId] +LEFT JOIN [OptionalRelated_OptionalNested] AS [o0] ON [o].[RootEntityId] = [o0].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_RequiredNested] AS [o1] ON [o].[RootEntityId] = [o1].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated] AS [r1] ON [r].[Id] = [r1].[RootEntityId] +LEFT JOIN [RequiredRelated_OptionalNested] AS [r2] ON [r1].[RootEntityId] = [r2].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated_RequiredNested] AS [r3] ON [r1].[RootEntityId] = [r3].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o2] ON [o].[RootEntityId] = [o2].[RelatedTypeRootEntityId] +LEFT JOIN ( + SELECT [r4].[RootEntityId], [r4].[Id], [r4].[Int], [r4].[Name], [r4].[String], [r5].[RelatedTypeRootEntityId], [r5].[RelatedTypeId], [r6].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId0], [r6].[RelatedTypeId] AS [RelatedTypeId0], [r7].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId1], [r7].[RelatedTypeId] AS [RelatedTypeId1], [r7].[Id] AS [Id0], [r7].[Int] AS [Int0], [r7].[Name] AS [Name0], [r7].[String] AS [String0], [r5].[Id] AS [Id1], [r5].[Int] AS [Int1], [r5].[Name] AS [Name1], [r5].[String] AS [String1], [r6].[Id] AS [Id2], [r6].[Int] AS [Int2], [r6].[Name] AS [Name2], [r6].[String] AS [String2] + FROM [RelatedCollection] AS [r4] + LEFT JOIN [RelatedCollection_OptionalNested] AS [r5] ON [r4].[RootEntityId] = [r5].[RelatedTypeRootEntityId] AND [r4].[Id] = [r5].[RelatedTypeId] + LEFT JOIN [RelatedCollection_RequiredNested] AS [r6] ON [r4].[RootEntityId] = [r6].[RelatedTypeRootEntityId] AND [r4].[Id] = [r6].[RelatedTypeId] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r7] ON [r4].[RootEntityId] = [r7].[RelatedTypeRootEntityId] AND [r4].[Id] = [r7].[RelatedTypeId] +) AS [s] ON [r].[Id] = [s].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r8] ON [r1].[RootEntityId] = [r8].[RelatedTypeRootEntityId] +WHERE ( + SELECT [r0].[Int] + FROM [RelatedCollection] AS [r0] + WHERE [r].[Id] = [r0].[RootEntityId] + ORDER BY (SELECT 1) + OFFSET @i ROWS FETCH NEXT 1 ROWS ONLY) = 21 +ORDER BY [r].[Id], [o].[RootEntityId], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r1].[RootEntityId], [r2].[RelatedTypeRootEntityId], [r3].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [s].[RootEntityId], [s].[Id], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [r8].[RelatedTypeRootEntityId] +"""); + } + + public override async Task Index_column(bool async) + { + await base.Index_column(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [o].[RootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r1].[RootEntityId], [r2].[RelatedTypeRootEntityId], [r3].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [o2].[Int], [o2].[Name], [o2].[String], [o0].[Id], [o0].[Int], [o0].[Name], [o0].[String], [o1].[Id], [o1].[Int], [o1].[Name], [o1].[String], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [s].[Int0], [s].[Name0], [s].[String0], [s].[Id1], [s].[Int1], [s].[Name1], [s].[String1], [s].[Id2], [s].[Int2], [s].[Name2], [s].[String2], [r1].[Id], [r1].[Int], [r1].[Name], [r1].[String], [r8].[RelatedTypeRootEntityId], [r8].[Id], [r8].[Int], [r8].[Name], [r8].[String], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String], [r3].[Id], [r3].[Int], [r3].[Name], [r3].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated] AS [o] ON [r].[Id] = [o].[RootEntityId] +LEFT JOIN [OptionalRelated_OptionalNested] AS [o0] ON [o].[RootEntityId] = [o0].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_RequiredNested] AS [o1] ON [o].[RootEntityId] = [o1].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated] AS [r1] ON [r].[Id] = [r1].[RootEntityId] +LEFT JOIN [RequiredRelated_OptionalNested] AS [r2] ON [r1].[RootEntityId] = [r2].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated_RequiredNested] AS [r3] ON [r1].[RootEntityId] = [r3].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o2] ON [o].[RootEntityId] = [o2].[RelatedTypeRootEntityId] +LEFT JOIN ( + SELECT [r4].[RootEntityId], [r4].[Id], [r4].[Int], [r4].[Name], [r4].[String], [r5].[RelatedTypeRootEntityId], [r5].[RelatedTypeId], [r6].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId0], [r6].[RelatedTypeId] AS [RelatedTypeId0], [r7].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId1], [r7].[RelatedTypeId] AS [RelatedTypeId1], [r7].[Id] AS [Id0], [r7].[Int] AS [Int0], [r7].[Name] AS [Name0], [r7].[String] AS [String0], [r5].[Id] AS [Id1], [r5].[Int] AS [Int1], [r5].[Name] AS [Name1], [r5].[String] AS [String1], [r6].[Id] AS [Id2], [r6].[Int] AS [Int2], [r6].[Name] AS [Name2], [r6].[String] AS [String2] + FROM [RelatedCollection] AS [r4] + LEFT JOIN [RelatedCollection_OptionalNested] AS [r5] ON [r4].[RootEntityId] = [r5].[RelatedTypeRootEntityId] AND [r4].[Id] = [r5].[RelatedTypeId] + LEFT JOIN [RelatedCollection_RequiredNested] AS [r6] ON [r4].[RootEntityId] = [r6].[RelatedTypeRootEntityId] AND [r4].[Id] = [r6].[RelatedTypeId] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r7] ON [r4].[RootEntityId] = [r7].[RelatedTypeRootEntityId] AND [r4].[Id] = [r7].[RelatedTypeId] +) AS [s] ON [r].[Id] = [s].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r8] ON [r1].[RootEntityId] = [r8].[RelatedTypeRootEntityId] +WHERE ( + SELECT [r0].[Int] + FROM [RelatedCollection] AS [r0] + WHERE [r].[Id] = [r0].[RootEntityId] + ORDER BY (SELECT 1) + OFFSET [r].[Id] - 1 ROWS FETCH NEXT 1 ROWS ONLY) = 21 +ORDER BY [r].[Id], [o].[RootEntityId], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r1].[RootEntityId], [r2].[RelatedTypeRootEntityId], [r3].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [s].[RootEntityId], [s].[Id], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [r8].[RelatedTypeRootEntityId] +"""); + } + + public override async Task Index_out_of_bounds(bool async) + { + await base.Index_out_of_bounds(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [o].[RootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r1].[RootEntityId], [r2].[RelatedTypeRootEntityId], [r3].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [o2].[Int], [o2].[Name], [o2].[String], [o0].[Id], [o0].[Int], [o0].[Name], [o0].[String], [o1].[Id], [o1].[Int], [o1].[Name], [o1].[String], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [s].[Int0], [s].[Name0], [s].[String0], [s].[Id1], [s].[Int1], [s].[Name1], [s].[String1], [s].[Id2], [s].[Int2], [s].[Name2], [s].[String2], [r1].[Id], [r1].[Int], [r1].[Name], [r1].[String], [r8].[RelatedTypeRootEntityId], [r8].[Id], [r8].[Int], [r8].[Name], [r8].[String], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String], [r3].[Id], [r3].[Int], [r3].[Name], [r3].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated] AS [o] ON [r].[Id] = [o].[RootEntityId] +LEFT JOIN [OptionalRelated_OptionalNested] AS [o0] ON [o].[RootEntityId] = [o0].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_RequiredNested] AS [o1] ON [o].[RootEntityId] = [o1].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated] AS [r1] ON [r].[Id] = [r1].[RootEntityId] +LEFT JOIN [RequiredRelated_OptionalNested] AS [r2] ON [r1].[RootEntityId] = [r2].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated_RequiredNested] AS [r3] ON [r1].[RootEntityId] = [r3].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o2] ON [o].[RootEntityId] = [o2].[RelatedTypeRootEntityId] +LEFT JOIN ( + SELECT [r4].[RootEntityId], [r4].[Id], [r4].[Int], [r4].[Name], [r4].[String], [r5].[RelatedTypeRootEntityId], [r5].[RelatedTypeId], [r6].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId0], [r6].[RelatedTypeId] AS [RelatedTypeId0], [r7].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId1], [r7].[RelatedTypeId] AS [RelatedTypeId1], [r7].[Id] AS [Id0], [r7].[Int] AS [Int0], [r7].[Name] AS [Name0], [r7].[String] AS [String0], [r5].[Id] AS [Id1], [r5].[Int] AS [Int1], [r5].[Name] AS [Name1], [r5].[String] AS [String1], [r6].[Id] AS [Id2], [r6].[Int] AS [Int2], [r6].[Name] AS [Name2], [r6].[String] AS [String2] + FROM [RelatedCollection] AS [r4] + LEFT JOIN [RelatedCollection_OptionalNested] AS [r5] ON [r4].[RootEntityId] = [r5].[RelatedTypeRootEntityId] AND [r4].[Id] = [r5].[RelatedTypeId] + LEFT JOIN [RelatedCollection_RequiredNested] AS [r6] ON [r4].[RootEntityId] = [r6].[RelatedTypeRootEntityId] AND [r4].[Id] = [r6].[RelatedTypeId] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r7] ON [r4].[RootEntityId] = [r7].[RelatedTypeRootEntityId] AND [r4].[Id] = [r7].[RelatedTypeId] +) AS [s] ON [r].[Id] = [s].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r8] ON [r1].[RootEntityId] = [r8].[RelatedTypeRootEntityId] +WHERE ( + SELECT [r0].[Int] + FROM [RelatedCollection] AS [r0] + WHERE [r].[Id] = [r0].[RootEntityId] + ORDER BY (SELECT 1) + OFFSET 9999 ROWS FETCH NEXT 1 ROWS ONLY) = 50 +ORDER BY [r].[Id], [o].[RootEntityId], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r1].[RootEntityId], [r2].[RelatedTypeRootEntityId], [r3].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [s].[RootEntityId], [s].[Id], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [r8].[RelatedTypeRootEntityId] +"""); + } + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsMiscellaneousSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsMiscellaneousSqlServerTest.cs new file mode 100644 index 00000000000..d77c780ad18 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsMiscellaneousSqlServerTest.cs @@ -0,0 +1,104 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; + +public class OwnedNavigationsMiscellaneousSqlServerTest( + OwnedNavigationsSqlServerFixture fixture, + ITestOutputHelper testOutputHelper) + : OwnedNavigationsMiscellaneousRelationalTestBase(fixture, testOutputHelper) +{ + #region Simple filters + + public override async Task Where_related_property(bool async) + { + await base.Where_related_property(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [o].[RootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String], [r0].[RootEntityId], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r1].[RelatedTypeRootEntityId], [r2].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [o2].[Int], [o2].[Name], [o2].[String], [o0].[Id], [o0].[Int], [o0].[Name], [o0].[String], [o1].[Id], [o1].[Int], [o1].[Name], [o1].[String], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [s].[Int0], [s].[Name0], [s].[String0], [s].[Id1], [s].[Int1], [s].[Name1], [s].[String1], [s].[Id2], [s].[Int2], [s].[Name2], [s].[String2], [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String], [r7].[RelatedTypeRootEntityId], [r7].[Id], [r7].[Int], [r7].[Name], [r7].[String], [r1].[Id], [r1].[Int], [r1].[Name], [r1].[String], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RequiredRelated] AS [r0] ON [r].[Id] = [r0].[RootEntityId] +LEFT JOIN [OptionalRelated] AS [o] ON [r].[Id] = [o].[RootEntityId] +LEFT JOIN [OptionalRelated_OptionalNested] AS [o0] ON [o].[RootEntityId] = [o0].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_RequiredNested] AS [o1] ON [o].[RootEntityId] = [o1].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated_OptionalNested] AS [r1] ON [r0].[RootEntityId] = [r1].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated_RequiredNested] AS [r2] ON [r0].[RootEntityId] = [r2].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o2] ON [o].[RootEntityId] = [o2].[RelatedTypeRootEntityId] +LEFT JOIN ( + SELECT [r3].[RootEntityId], [r3].[Id], [r3].[Int], [r3].[Name], [r3].[String], [r4].[RelatedTypeRootEntityId], [r4].[RelatedTypeId], [r5].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId0], [r5].[RelatedTypeId] AS [RelatedTypeId0], [r6].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId1], [r6].[RelatedTypeId] AS [RelatedTypeId1], [r6].[Id] AS [Id0], [r6].[Int] AS [Int0], [r6].[Name] AS [Name0], [r6].[String] AS [String0], [r4].[Id] AS [Id1], [r4].[Int] AS [Int1], [r4].[Name] AS [Name1], [r4].[String] AS [String1], [r5].[Id] AS [Id2], [r5].[Int] AS [Int2], [r5].[Name] AS [Name2], [r5].[String] AS [String2] + FROM [RelatedCollection] AS [r3] + LEFT JOIN [RelatedCollection_OptionalNested] AS [r4] ON [r3].[RootEntityId] = [r4].[RelatedTypeRootEntityId] AND [r3].[Id] = [r4].[RelatedTypeId] + LEFT JOIN [RelatedCollection_RequiredNested] AS [r5] ON [r3].[RootEntityId] = [r5].[RelatedTypeRootEntityId] AND [r3].[Id] = [r5].[RelatedTypeId] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r6] ON [r3].[RootEntityId] = [r6].[RelatedTypeRootEntityId] AND [r3].[Id] = [r6].[RelatedTypeId] +) AS [s] ON [r].[Id] = [s].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r7] ON [r0].[RootEntityId] = [r7].[RelatedTypeRootEntityId] +WHERE [r0].[Int] = 8 +ORDER BY [r].[Id], [r0].[RootEntityId], [o].[RootEntityId], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r1].[RelatedTypeRootEntityId], [r2].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [s].[RootEntityId], [s].[Id], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [r7].[RelatedTypeRootEntityId] +"""); + } + + public override async Task Where_optional_related_property(bool async) + { + await base.Where_optional_related_property(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [o].[RootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r0].[RootEntityId], [r1].[RelatedTypeRootEntityId], [r2].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [o2].[Int], [o2].[Name], [o2].[String], [o0].[Id], [o0].[Int], [o0].[Name], [o0].[String], [o1].[Id], [o1].[Int], [o1].[Name], [o1].[String], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [s].[Int0], [s].[Name0], [s].[String0], [s].[Id1], [s].[Int1], [s].[Name1], [s].[String1], [s].[Id2], [s].[Int2], [s].[Name2], [s].[String2], [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String], [r7].[RelatedTypeRootEntityId], [r7].[Id], [r7].[Int], [r7].[Name], [r7].[String], [r1].[Id], [r1].[Int], [r1].[Name], [r1].[String], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated] AS [o] ON [r].[Id] = [o].[RootEntityId] +LEFT JOIN [OptionalRelated_OptionalNested] AS [o0] ON [o].[RootEntityId] = [o0].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_RequiredNested] AS [o1] ON [o].[RootEntityId] = [o1].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated] AS [r0] ON [r].[Id] = [r0].[RootEntityId] +LEFT JOIN [RequiredRelated_OptionalNested] AS [r1] ON [r0].[RootEntityId] = [r1].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated_RequiredNested] AS [r2] ON [r0].[RootEntityId] = [r2].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o2] ON [o].[RootEntityId] = [o2].[RelatedTypeRootEntityId] +LEFT JOIN ( + SELECT [r3].[RootEntityId], [r3].[Id], [r3].[Int], [r3].[Name], [r3].[String], [r4].[RelatedTypeRootEntityId], [r4].[RelatedTypeId], [r5].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId0], [r5].[RelatedTypeId] AS [RelatedTypeId0], [r6].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId1], [r6].[RelatedTypeId] AS [RelatedTypeId1], [r6].[Id] AS [Id0], [r6].[Int] AS [Int0], [r6].[Name] AS [Name0], [r6].[String] AS [String0], [r4].[Id] AS [Id1], [r4].[Int] AS [Int1], [r4].[Name] AS [Name1], [r4].[String] AS [String1], [r5].[Id] AS [Id2], [r5].[Int] AS [Int2], [r5].[Name] AS [Name2], [r5].[String] AS [String2] + FROM [RelatedCollection] AS [r3] + LEFT JOIN [RelatedCollection_OptionalNested] AS [r4] ON [r3].[RootEntityId] = [r4].[RelatedTypeRootEntityId] AND [r3].[Id] = [r4].[RelatedTypeId] + LEFT JOIN [RelatedCollection_RequiredNested] AS [r5] ON [r3].[RootEntityId] = [r5].[RelatedTypeRootEntityId] AND [r3].[Id] = [r5].[RelatedTypeId] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r6] ON [r3].[RootEntityId] = [r6].[RelatedTypeRootEntityId] AND [r3].[Id] = [r6].[RelatedTypeId] +) AS [s] ON [r].[Id] = [s].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r7] ON [r0].[RootEntityId] = [r7].[RelatedTypeRootEntityId] +WHERE [o].[Int] = 9 +ORDER BY [r].[Id], [o].[RootEntityId], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r0].[RootEntityId], [r1].[RelatedTypeRootEntityId], [r2].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [s].[RootEntityId], [s].[Id], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [r7].[RelatedTypeRootEntityId] +"""); + } + + public override async Task Where_nested_related_property(bool async) + { + await base.Where_nested_related_property(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [o].[RootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String], [r0].[RootEntityId], [r1].[RelatedTypeRootEntityId], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r2].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [o2].[Int], [o2].[Name], [o2].[String], [o0].[Id], [o0].[Int], [o0].[Name], [o0].[String], [o1].[Id], [o1].[Int], [o1].[Name], [o1].[String], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [s].[Int0], [s].[Name0], [s].[String0], [s].[Id1], [s].[Int1], [s].[Name1], [s].[String1], [s].[Id2], [s].[Int2], [s].[Name2], [s].[String2], [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String], [r7].[RelatedTypeRootEntityId], [r7].[Id], [r7].[Int], [r7].[Name], [r7].[String], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String], [r1].[Id], [r1].[Int], [r1].[Name], [r1].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RequiredRelated] AS [r0] ON [r].[Id] = [r0].[RootEntityId] +LEFT JOIN [RequiredRelated_RequiredNested] AS [r1] ON [r0].[RootEntityId] = [r1].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated] AS [o] ON [r].[Id] = [o].[RootEntityId] +LEFT JOIN [OptionalRelated_OptionalNested] AS [o0] ON [o].[RootEntityId] = [o0].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_RequiredNested] AS [o1] ON [o].[RootEntityId] = [o1].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated_OptionalNested] AS [r2] ON [r0].[RootEntityId] = [r2].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o2] ON [o].[RootEntityId] = [o2].[RelatedTypeRootEntityId] +LEFT JOIN ( + SELECT [r3].[RootEntityId], [r3].[Id], [r3].[Int], [r3].[Name], [r3].[String], [r4].[RelatedTypeRootEntityId], [r4].[RelatedTypeId], [r5].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId0], [r5].[RelatedTypeId] AS [RelatedTypeId0], [r6].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId1], [r6].[RelatedTypeId] AS [RelatedTypeId1], [r6].[Id] AS [Id0], [r6].[Int] AS [Int0], [r6].[Name] AS [Name0], [r6].[String] AS [String0], [r4].[Id] AS [Id1], [r4].[Int] AS [Int1], [r4].[Name] AS [Name1], [r4].[String] AS [String1], [r5].[Id] AS [Id2], [r5].[Int] AS [Int2], [r5].[Name] AS [Name2], [r5].[String] AS [String2] + FROM [RelatedCollection] AS [r3] + LEFT JOIN [RelatedCollection_OptionalNested] AS [r4] ON [r3].[RootEntityId] = [r4].[RelatedTypeRootEntityId] AND [r3].[Id] = [r4].[RelatedTypeId] + LEFT JOIN [RelatedCollection_RequiredNested] AS [r5] ON [r3].[RootEntityId] = [r5].[RelatedTypeRootEntityId] AND [r3].[Id] = [r5].[RelatedTypeId] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r6] ON [r3].[RootEntityId] = [r6].[RelatedTypeRootEntityId] AND [r3].[Id] = [r6].[RelatedTypeId] +) AS [s] ON [r].[Id] = [s].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r7] ON [r0].[RootEntityId] = [r7].[RelatedTypeRootEntityId] +WHERE [r1].[Int] = 50 +ORDER BY [r].[Id], [r0].[RootEntityId], [r1].[RelatedTypeRootEntityId], [o].[RootEntityId], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r2].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [s].[RootEntityId], [s].[Id], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [r7].[RelatedTypeRootEntityId] +"""); + } + + #endregion Simple filters + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionSqlServerTest.cs index 73e34a9d728..b7116ff2604 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsProjectionSqlServerTest.cs @@ -3,794 +3,380 @@ namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; -public class OwnedNavigationsProjectionSqlServerTest - : OwnedNavigationsProjectionRelationalTestBase +public class OwnedNavigationsProjectionSqlServerTest(OwnedNavigationsSqlServerFixture fixture, ITestOutputHelper testOutputHelper) + : OwnedNavigationsProjectionRelationalTestBase(fixture, testOutputHelper) { - public OwnedNavigationsProjectionSqlServerTest(OwnedNavigationsSqlServerFixture fixture, ITestOutputHelper testOutputHelper) - : base(fixture) - { - Fixture.TestSqlLoggerFactory.Clear(); - Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); - } - public override async Task Select_root(bool async, QueryTrackingBehavior queryTrackingBehavior) { await base.Select_root(async, queryTrackingBehavior); AssertSql( """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [s0].[RelationshipsRootId], [s0].[Id1], [s0].[Name], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsTrunkId1], [s0].[Id10], [s0].[Name0], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchRelationshipsTrunkId1], [s0].[RelationshipsBranchId1], [s0].[Id100], [s0].[Name00], [s0].[OptionalReferenceLeaf_Name], [s0].[RequiredReferenceLeaf_Name], [s0].[OptionalReferenceBranch_Name], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchRelationshipsTrunkId10], [s0].[Id11], [s0].[Name1], [s0].[OptionalReferenceBranch_OptionalReferenceLeaf_Name], [s0].[OptionalReferenceBranch_RequiredReferenceLeaf_Name], [s0].[RequiredReferenceBranch_Name], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchRelationshipsTrunkId11], [s0].[Id12], [s0].[Name2], [s0].[RequiredReferenceBranch_OptionalReferenceLeaf_Name], [s0].[RequiredReferenceBranch_RequiredReferenceLeaf_Name], [r].[OptionalReferenceTrunk_Name], [s1].[RelationshipsTrunkRelationshipsRootId], [s1].[Id1], [s1].[Name], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsBranchId1], [s1].[Id10], [s1].[Name0], [s1].[OptionalReferenceLeaf_Name], [s1].[RequiredReferenceLeaf_Name], [r].[OptionalReferenceTrunk_OptionalReferenceBranch_Name], [r7].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r7].[Id1], [r7].[Name], [r].[OptionalReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r].[OptionalReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf_Name], [r].[OptionalReferenceTrunk_RequiredReferenceBranch_Name], [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r8].[Id1], [r8].[Name], [r].[OptionalReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r].[OptionalReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf_Name], [r].[RequiredReferenceTrunk_Name], [s2].[RelationshipsTrunkRelationshipsRootId], [s2].[Id1], [s2].[Name], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s2].[RelationshipsBranchId1], [s2].[Id10], [s2].[Name0], [s2].[OptionalReferenceLeaf_Name], [s2].[RequiredReferenceLeaf_Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_Name], [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[Id1], [r11].[Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf_Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_Name], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r12].[Id1], [r12].[Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -LEFT JOIN ( - SELECT [r0].[RelationshipsRootId], [r0].[Id1], [r0].[Name], [s].[RelationshipsTrunkRelationshipsRootId], [s].[RelationshipsTrunkId1], [s].[Id1] AS [Id10], [s].[Name] AS [Name0], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchRelationshipsTrunkId1], [s].[RelationshipsBranchId1], [s].[Id10] AS [Id100], [s].[Name0] AS [Name00], [s].[OptionalReferenceLeaf_Name], [s].[RequiredReferenceLeaf_Name], [r0].[OptionalReferenceBranch_Name], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r3].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r3].[Id1] AS [Id11], [r3].[Name] AS [Name1], [r0].[OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r0].[OptionalReferenceBranch_RequiredReferenceLeaf_Name], [r0].[RequiredReferenceBranch_Name], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r4].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r4].[Id1] AS [Id12], [r4].[Name] AS [Name2], [r0].[RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r0].[RequiredReferenceBranch_RequiredReferenceLeaf_Name] - FROM [Root_CollectionTrunk] AS [r0] - LEFT JOIN ( - SELECT [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[RelationshipsTrunkId1], [r1].[Id1], [r1].[Name], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkId1], [r2].[RelationshipsBranchId1], [r2].[Id1] AS [Id10], [r2].[Name] AS [Name0], [r1].[OptionalReferenceLeaf_Name], [r1].[RequiredReferenceLeaf_Name] - FROM [Root_CollectionTrunk_CollectionBranch] AS [r1] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_CollectionLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[RelationshipsTrunkId1] = [r2].[RelationshipsBranchRelationshipsTrunkId1] AND [r1].[Id1] = [r2].[RelationshipsBranchId1] - ) AS [s] ON [r0].[RelationshipsRootId] = [s].[RelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [s].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r3] ON CASE - WHEN [r0].[OptionalReferenceBranch_Name] IS NOT NULL THEN [r0].[RelationshipsRootId] - END = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND CASE - WHEN [r0].[OptionalReferenceBranch_Name] IS NOT NULL THEN [r0].[Id1] - END = [r3].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r4] ON [r0].[RelationshipsRootId] = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [r4].[RelationshipsBranchRelationshipsTrunkId1] -) AS [s0] ON [r].[Id] = [s0].[RelationshipsRootId] -LEFT JOIN ( - SELECT [r5].[RelationshipsTrunkRelationshipsRootId], [r5].[Id1], [r5].[Name], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchId1], [r6].[Id1] AS [Id10], [r6].[Name] AS [Name0], [r5].[OptionalReferenceLeaf_Name], [r5].[RequiredReferenceLeaf_Name] - FROM [Root_OptionalReferenceTrunk_CollectionBranch] AS [r5] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r6] ON [r5].[RelationshipsTrunkRelationshipsRootId] = [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r5].[Id1] = [r6].[RelationshipsBranchId1] -) AS [s1] ON CASE - WHEN [r].[OptionalReferenceTrunk_Name] IS NOT NULL THEN [r].[Id] -END = [s1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r7] ON CASE - WHEN [r].[OptionalReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r7].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r8] ON CASE - WHEN [r].[OptionalReferenceTrunk_RequiredReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] +SELECT [r].[Id], [r].[Name], [o].[RootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r0].[RootEntityId], [r1].[RelatedTypeRootEntityId], [r2].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [o2].[Int], [o2].[Name], [o2].[String], [o0].[Id], [o0].[Int], [o0].[Name], [o0].[String], [o1].[Id], [o1].[Int], [o1].[Name], [o1].[String], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [s].[Int0], [s].[Name0], [s].[String0], [s].[Id1], [s].[Int1], [s].[Name1], [s].[String1], [s].[Id2], [s].[Int2], [s].[Name2], [s].[String2], [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String], [r7].[RelatedTypeRootEntityId], [r7].[Id], [r7].[Int], [r7].[Name], [r7].[String], [r1].[Id], [r1].[Int], [r1].[Name], [r1].[String], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated] AS [o] ON [r].[Id] = [o].[RootEntityId] +LEFT JOIN [OptionalRelated_OptionalNested] AS [o0] ON [o].[RootEntityId] = [o0].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_RequiredNested] AS [o1] ON [o].[RootEntityId] = [o1].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated] AS [r0] ON [r].[Id] = [r0].[RootEntityId] +LEFT JOIN [RequiredRelated_OptionalNested] AS [r1] ON [r0].[RootEntityId] = [r1].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated_RequiredNested] AS [r2] ON [r0].[RootEntityId] = [r2].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o2] ON [o].[RootEntityId] = [o2].[RelatedTypeRootEntityId] LEFT JOIN ( - SELECT [r9].[RelationshipsTrunkRelationshipsRootId], [r9].[Id1], [r9].[Name], [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r10].[RelationshipsBranchId1], [r10].[Id1] AS [Id10], [r10].[Name] AS [Name0], [r9].[OptionalReferenceLeaf_Name], [r9].[RequiredReferenceLeaf_Name] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r9] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r10] ON [r9].[RelationshipsTrunkRelationshipsRootId] = [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r9].[Id1] = [r10].[RelationshipsBranchId1] -) AS [s2] ON [r].[Id] = [s2].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r11] ON CASE - WHEN [r].[RequiredReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r12] ON [r].[Id] = [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [s0].[RelationshipsRootId], [s0].[Id1], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsTrunkId1], [s0].[Id10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchRelationshipsTrunkId1], [s0].[RelationshipsBranchId1], [s0].[Id100], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchRelationshipsTrunkId10], [s0].[Id11], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchRelationshipsTrunkId11], [s0].[Id12], [s1].[RelationshipsTrunkRelationshipsRootId], [s1].[Id1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsBranchId1], [s1].[Id10], [r7].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r7].[Id1], [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r8].[Id1], [s2].[RelationshipsTrunkRelationshipsRootId], [s2].[Id1], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s2].[RelationshipsBranchId1], [s2].[Id10], [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[Id1], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] + SELECT [r3].[RootEntityId], [r3].[Id], [r3].[Int], [r3].[Name], [r3].[String], [r4].[RelatedTypeRootEntityId], [r4].[RelatedTypeId], [r5].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId0], [r5].[RelatedTypeId] AS [RelatedTypeId0], [r6].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId1], [r6].[RelatedTypeId] AS [RelatedTypeId1], [r6].[Id] AS [Id0], [r6].[Int] AS [Int0], [r6].[Name] AS [Name0], [r6].[String] AS [String0], [r4].[Id] AS [Id1], [r4].[Int] AS [Int1], [r4].[Name] AS [Name1], [r4].[String] AS [String1], [r5].[Id] AS [Id2], [r5].[Int] AS [Int2], [r5].[Name] AS [Name2], [r5].[String] AS [String2] + FROM [RelatedCollection] AS [r3] + LEFT JOIN [RelatedCollection_OptionalNested] AS [r4] ON [r3].[RootEntityId] = [r4].[RelatedTypeRootEntityId] AND [r3].[Id] = [r4].[RelatedTypeId] + LEFT JOIN [RelatedCollection_RequiredNested] AS [r5] ON [r3].[RootEntityId] = [r5].[RelatedTypeRootEntityId] AND [r3].[Id] = [r5].[RelatedTypeId] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r6] ON [r3].[RootEntityId] = [r6].[RelatedTypeRootEntityId] AND [r3].[Id] = [r6].[RelatedTypeId] +) AS [s] ON [r].[Id] = [s].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r7] ON [r0].[RootEntityId] = [r7].[RelatedTypeRootEntityId] +ORDER BY [r].[Id], [o].[RootEntityId], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r0].[RootEntityId], [r1].[RelatedTypeRootEntityId], [r2].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [s].[RootEntityId], [s].[Id], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [r7].[RelatedTypeRootEntityId] """); } - public override async Task Select_trunk_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_trunk_optional(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r].[Id], [r].[OptionalReferenceTrunk_Name], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[Id10], [s].[Name0], [s].[OptionalReferenceLeaf_Name], [s].[RequiredReferenceLeaf_Name], [r].[OptionalReferenceTrunk_OptionalReferenceBranch_Name], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[Id1], [r2].[Name], [r].[OptionalReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r].[OptionalReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf_Name], [r].[OptionalReferenceTrunk_RequiredReferenceBranch_Name], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[Id1], [r3].[Name], [r].[OptionalReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r].[OptionalReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -LEFT JOIN ( - SELECT [r0].[RelationshipsTrunkRelationshipsRootId], [r0].[Id1], [r0].[Name], [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r1].[RelationshipsBranchId1], [r1].[Id1] AS [Id10], [r1].[Name] AS [Name0], [r0].[OptionalReferenceLeaf_Name], [r0].[RequiredReferenceLeaf_Name] - FROM [Root_OptionalReferenceTrunk_CollectionBranch] AS [r0] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r1] ON [r0].[RelationshipsTrunkRelationshipsRootId] = [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [r1].[RelationshipsBranchId1] -) AS [s] ON CASE - WHEN [r].[OptionalReferenceTrunk_Name] IS NOT NULL THEN [r].[Id] -END = [s].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r2] ON CASE - WHEN [r].[OptionalReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r3] ON CASE - WHEN [r].[OptionalReferenceTrunk_RequiredReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[Id10], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[Id1], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -"""); - } - } + #region Simple properties - public override async Task Select_trunk_required(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_related_property(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_trunk_required(async, queryTrackingBehavior); + await base.Select_related_property(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r].[Id], [r].[RequiredReferenceTrunk_Name], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[Id10], [s].[Name0], [s].[OptionalReferenceLeaf_Name], [s].[RequiredReferenceLeaf_Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_Name], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[Id1], [r2].[Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf_Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_Name], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[Id1], [r3].[Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -LEFT JOIN ( - SELECT [r0].[RelationshipsTrunkRelationshipsRootId], [r0].[Id1], [r0].[Name], [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r1].[RelationshipsBranchId1], [r1].[Id1] AS [Id10], [r1].[Name] AS [Name0], [r0].[OptionalReferenceLeaf_Name], [r0].[RequiredReferenceLeaf_Name] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r0] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r1] ON [r0].[RelationshipsTrunkRelationshipsRootId] = [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [r1].[RelationshipsBranchId1] -) AS [s] ON [r].[Id] = [s].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r2] ON CASE - WHEN [r].[RequiredReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r3] ON [r].[Id] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[Id10], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[Id1], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] + AssertSql( + """ +SELECT [r0].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RequiredRelated] AS [r0] ON [r].[Id] = [r0].[RootEntityId] """); - } } - public override async Task Select_trunk_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_property(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_trunk_collection(async, queryTrackingBehavior); + await base.Select_optional_related_property(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r].[Id], [s0].[RelationshipsRootId], [s0].[Id1], [s0].[Name], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsTrunkId1], [s0].[Id10], [s0].[Name0], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchRelationshipsTrunkId1], [s0].[RelationshipsBranchId1], [s0].[Id100], [s0].[Name00], [s0].[OptionalReferenceLeaf_Name], [s0].[RequiredReferenceLeaf_Name], [s0].[OptionalReferenceBranch_Name], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchRelationshipsTrunkId10], [s0].[Id11], [s0].[Name1], [s0].[OptionalReferenceBranch_OptionalReferenceLeaf_Name], [s0].[OptionalReferenceBranch_RequiredReferenceLeaf_Name], [s0].[RequiredReferenceBranch_Name], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchRelationshipsTrunkId11], [s0].[Id12], [s0].[Name2], [s0].[RequiredReferenceBranch_OptionalReferenceLeaf_Name], [s0].[RequiredReferenceBranch_RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -LEFT JOIN ( - SELECT [r0].[RelationshipsRootId], [r0].[Id1], [r0].[Name], [s].[RelationshipsTrunkRelationshipsRootId], [s].[RelationshipsTrunkId1], [s].[Id1] AS [Id10], [s].[Name] AS [Name0], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchRelationshipsTrunkId1], [s].[RelationshipsBranchId1], [s].[Id10] AS [Id100], [s].[Name0] AS [Name00], [s].[OptionalReferenceLeaf_Name], [s].[RequiredReferenceLeaf_Name], [r0].[OptionalReferenceBranch_Name], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r3].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r3].[Id1] AS [Id11], [r3].[Name] AS [Name1], [r0].[OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r0].[OptionalReferenceBranch_RequiredReferenceLeaf_Name], [r0].[RequiredReferenceBranch_Name], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r4].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r4].[Id1] AS [Id12], [r4].[Name] AS [Name2], [r0].[RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r0].[RequiredReferenceBranch_RequiredReferenceLeaf_Name] - FROM [Root_CollectionTrunk] AS [r0] - LEFT JOIN ( - SELECT [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[RelationshipsTrunkId1], [r1].[Id1], [r1].[Name], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkId1], [r2].[RelationshipsBranchId1], [r2].[Id1] AS [Id10], [r2].[Name] AS [Name0], [r1].[OptionalReferenceLeaf_Name], [r1].[RequiredReferenceLeaf_Name] - FROM [Root_CollectionTrunk_CollectionBranch] AS [r1] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_CollectionLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[RelationshipsTrunkId1] = [r2].[RelationshipsBranchRelationshipsTrunkId1] AND [r1].[Id1] = [r2].[RelationshipsBranchId1] - ) AS [s] ON [r0].[RelationshipsRootId] = [s].[RelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [s].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r3] ON CASE - WHEN [r0].[OptionalReferenceBranch_Name] IS NOT NULL THEN [r0].[RelationshipsRootId] - END = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND CASE - WHEN [r0].[OptionalReferenceBranch_Name] IS NOT NULL THEN [r0].[Id1] - END = [r3].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r4] ON [r0].[RelationshipsRootId] = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [r4].[RelationshipsBranchRelationshipsTrunkId1] -) AS [s0] ON [r].[Id] = [s0].[RelationshipsRootId] -ORDER BY [r].[Id], [s0].[RelationshipsRootId], [s0].[Id1], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsTrunkId1], [s0].[Id10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchRelationshipsTrunkId1], [s0].[RelationshipsBranchId1], [s0].[Id100], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchRelationshipsTrunkId10], [s0].[Id11], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchRelationshipsTrunkId11] + AssertSql( + """ +SELECT [o].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated] AS [o] ON [r].[Id] = [o].[RootEntityId] """); - } } - public override async Task Select_branch_required_required(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_property_value_type(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_branch_required_required(async, queryTrackingBehavior); + await base.Select_optional_related_property_value_type(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r].[Id], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_Name], [r0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r0].[Id1], [r0].[Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r0] ON [r].[Id] = [r0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] + AssertSql( + """ +SELECT [o].[Int] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated] AS [o] ON [r].[Id] = [o].[RootEntityId] """); - } } - public override async Task Select_branch_required_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_branch_required_optional(async, queryTrackingBehavior); + #endregion Simple properties - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r].[Id], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_Name], [r0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r0].[Id1], [r0].[Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r0] ON CASE - WHEN [r].[RequiredReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -"""); - } - } + #region Non-collection - public override async Task Select_branch_optional_required(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_related(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_branch_optional_required(async, queryTrackingBehavior); + await base.Select_related(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r].[Id], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_Name], [r0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r0].[Id1], [r0].[Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r0] ON [r].[Id] = [r0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] +SELECT [r0].[RootEntityId], [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String], [r].[Id], [r1].[RelatedTypeRootEntityId], [r2].[RelatedTypeRootEntityId], [r3].[RelatedTypeRootEntityId], [r3].[Id], [r3].[Int], [r3].[Name], [r3].[String], [r1].[Id], [r1].[Int], [r1].[Name], [r1].[String], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RequiredRelated] AS [r0] ON [r].[Id] = [r0].[RootEntityId] +LEFT JOIN [RequiredRelated_OptionalNested] AS [r1] ON [r0].[RootEntityId] = [r1].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated_RequiredNested] AS [r2] ON [r0].[RootEntityId] = [r2].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r3] ON [r0].[RootEntityId] = [r3].[RelatedTypeRootEntityId] +ORDER BY [r].[Id], [r0].[RootEntityId], [r1].[RelatedTypeRootEntityId], [r2].[RelatedTypeRootEntityId], [r3].[RelatedTypeRootEntityId] """); } } - public override async Task Select_branch_optional_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_branch_optional_optional(async, queryTrackingBehavior); + await base.Select_optional_related(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r].[Id], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_Name], [r0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r0].[Id1], [r0].[Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r0] ON CASE - WHEN [r].[RequiredReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] +SELECT [o].[RootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String], [r].[Id], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [o2].[Int], [o2].[Name], [o2].[String], [o0].[Id], [o0].[Int], [o0].[Name], [o0].[String], [o1].[Id], [o1].[Int], [o1].[Name], [o1].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated] AS [o] ON [r].[Id] = [o].[RootEntityId] +LEFT JOIN [OptionalRelated_OptionalNested] AS [o0] ON [o].[RootEntityId] = [o0].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_RequiredNested] AS [o1] ON [o].[RootEntityId] = [o1].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o2] ON [o].[RootEntityId] = [o2].[RelatedTypeRootEntityId] +ORDER BY [r].[Id], [o].[RootEntityId], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId] """); } } - public override async Task Select_branch_required_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_required_related_required_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_branch_optional_optional(async, queryTrackingBehavior); + await base.Select_required_related_required_nested(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r].[Id], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_Name], [r0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r0].[Id1], [r0].[Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r0] ON CASE - WHEN [r].[RequiredReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] +SELECT [r1].[RelatedTypeRootEntityId], [r1].[Id], [r1].[Int], [r1].[Name], [r1].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RequiredRelated] AS [r0] ON [r].[Id] = [r0].[RootEntityId] +LEFT JOIN [RequiredRelated_RequiredNested] AS [r1] ON [r0].[RootEntityId] = [r1].[RelatedTypeRootEntityId] """); } } - public override async Task Select_branch_optional_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_required_related_optional_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_branch_optional_collection(async, queryTrackingBehavior); + await base.Select_required_related_optional_nested(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r].[Id], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[Id10], [s].[Name0], [s].[OptionalReferenceLeaf_Name], [s].[RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -LEFT JOIN ( - SELECT [r0].[RelationshipsTrunkRelationshipsRootId], [r0].[Id1], [r0].[Name], [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r1].[RelationshipsBranchId1], [r1].[Id1] AS [Id10], [r1].[Name] AS [Name0], [r0].[OptionalReferenceLeaf_Name], [r0].[RequiredReferenceLeaf_Name] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r0] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r1] ON [r0].[RelationshipsTrunkRelationshipsRootId] = [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [r1].[RelationshipsBranchId1] -) AS [s] ON [r].[Id] = [s].[RelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1] +SELECT [r1].[RelatedTypeRootEntityId], [r1].[Id], [r1].[Int], [r1].[Name], [r1].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RequiredRelated] AS [r0] ON [r].[Id] = [r0].[RootEntityId] +LEFT JOIN [RequiredRelated_OptionalNested] AS [r1] ON [r0].[RootEntityId] = [r1].[RelatedTypeRootEntityId] """); } } - #region Multiple - - public override async Task Select_root_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_required_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_root_duplicated(async, queryTrackingBehavior); + await base.Select_optional_related_required_nested(async, queryTrackingBehavior); - AssertSql( - """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [s0].[RelationshipsRootId], [s0].[Id1], [s0].[Name], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsTrunkId1], [s0].[Id10], [s0].[Name0], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchRelationshipsTrunkId1], [s0].[RelationshipsBranchId1], [s0].[Id100], [s0].[Name00], [s0].[OptionalReferenceLeaf_Name], [s0].[RequiredReferenceLeaf_Name], [s0].[OptionalReferenceBranch_Name], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchRelationshipsTrunkId10], [s0].[Id11], [s0].[Name1], [s0].[OptionalReferenceBranch_OptionalReferenceLeaf_Name], [s0].[OptionalReferenceBranch_RequiredReferenceLeaf_Name], [s0].[RequiredReferenceBranch_Name], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchRelationshipsTrunkId11], [s0].[Id12], [s0].[Name2], [s0].[RequiredReferenceBranch_OptionalReferenceLeaf_Name], [s0].[RequiredReferenceBranch_RequiredReferenceLeaf_Name], [r].[OptionalReferenceTrunk_Name], [s1].[RelationshipsTrunkRelationshipsRootId], [s1].[Id1], [s1].[Name], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsBranchId1], [s1].[Id10], [s1].[Name0], [s1].[OptionalReferenceLeaf_Name], [s1].[RequiredReferenceLeaf_Name], [r].[OptionalReferenceTrunk_OptionalReferenceBranch_Name], [r7].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r7].[Id1], [r7].[Name], [r].[OptionalReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r].[OptionalReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf_Name], [r].[OptionalReferenceTrunk_RequiredReferenceBranch_Name], [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r8].[Id1], [r8].[Name], [r].[OptionalReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r].[OptionalReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf_Name], [r].[RequiredReferenceTrunk_Name], [s2].[RelationshipsTrunkRelationshipsRootId], [s2].[Id1], [s2].[Name], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s2].[RelationshipsBranchId1], [s2].[Id10], [s2].[Name0], [s2].[OptionalReferenceLeaf_Name], [s2].[RequiredReferenceLeaf_Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_Name], [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[Id1], [r11].[Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf_Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_Name], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r12].[Id1], [r12].[Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf_Name], [s4].[RelationshipsRootId], [s4].[Id1], [s4].[Name], [s4].[RelationshipsTrunkRelationshipsRootId], [s4].[RelationshipsTrunkId1], [s4].[Id10], [s4].[Name0], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s4].[RelationshipsBranchRelationshipsTrunkId1], [s4].[RelationshipsBranchId1], [s4].[Id100], [s4].[Name00], [s4].[OptionalReferenceLeaf_Name], [s4].[RequiredReferenceLeaf_Name], [s4].[OptionalReferenceBranch_Name], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s4].[RelationshipsBranchRelationshipsTrunkId10], [s4].[Id11], [s4].[Name1], [s4].[OptionalReferenceBranch_OptionalReferenceLeaf_Name], [s4].[OptionalReferenceBranch_RequiredReferenceLeaf_Name], [s4].[RequiredReferenceBranch_Name], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s4].[RelationshipsBranchRelationshipsTrunkId11], [s4].[Id12], [s4].[Name2], [s4].[RequiredReferenceBranch_OptionalReferenceLeaf_Name], [s4].[RequiredReferenceBranch_RequiredReferenceLeaf_Name], [s5].[RelationshipsTrunkRelationshipsRootId], [s5].[Id1], [s5].[Name], [s5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s5].[RelationshipsBranchId1], [s5].[Id10], [s5].[Name0], [s5].[OptionalReferenceLeaf_Name], [s5].[RequiredReferenceLeaf_Name], [r20].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r20].[Id1], [r20].[Name], [r21].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r21].[Id1], [r21].[Name], [s6].[RelationshipsTrunkRelationshipsRootId], [s6].[Id1], [s6].[Name], [s6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s6].[RelationshipsBranchId1], [s6].[Id10], [s6].[Name0], [s6].[OptionalReferenceLeaf_Name], [s6].[RequiredReferenceLeaf_Name], [r24].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r24].[Id1], [r24].[Name], [r25].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r25].[Id1], [r25].[Name] -FROM [RootEntities] AS [r] -LEFT JOIN ( - SELECT [r0].[RelationshipsRootId], [r0].[Id1], [r0].[Name], [s].[RelationshipsTrunkRelationshipsRootId], [s].[RelationshipsTrunkId1], [s].[Id1] AS [Id10], [s].[Name] AS [Name0], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchRelationshipsTrunkId1], [s].[RelationshipsBranchId1], [s].[Id10] AS [Id100], [s].[Name0] AS [Name00], [s].[OptionalReferenceLeaf_Name], [s].[RequiredReferenceLeaf_Name], [r0].[OptionalReferenceBranch_Name], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r3].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r3].[Id1] AS [Id11], [r3].[Name] AS [Name1], [r0].[OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r0].[OptionalReferenceBranch_RequiredReferenceLeaf_Name], [r0].[RequiredReferenceBranch_Name], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r4].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r4].[Id1] AS [Id12], [r4].[Name] AS [Name2], [r0].[RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r0].[RequiredReferenceBranch_RequiredReferenceLeaf_Name] - FROM [Root_CollectionTrunk] AS [r0] - LEFT JOIN ( - SELECT [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[RelationshipsTrunkId1], [r1].[Id1], [r1].[Name], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkId1], [r2].[RelationshipsBranchId1], [r2].[Id1] AS [Id10], [r2].[Name] AS [Name0], [r1].[OptionalReferenceLeaf_Name], [r1].[RequiredReferenceLeaf_Name] - FROM [Root_CollectionTrunk_CollectionBranch] AS [r1] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_CollectionLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[RelationshipsTrunkId1] = [r2].[RelationshipsBranchRelationshipsTrunkId1] AND [r1].[Id1] = [r2].[RelationshipsBranchId1] - ) AS [s] ON [r0].[RelationshipsRootId] = [s].[RelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [s].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r3] ON CASE - WHEN [r0].[OptionalReferenceBranch_Name] IS NOT NULL THEN [r0].[RelationshipsRootId] - END = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND CASE - WHEN [r0].[OptionalReferenceBranch_Name] IS NOT NULL THEN [r0].[Id1] - END = [r3].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r4] ON [r0].[RelationshipsRootId] = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [r4].[RelationshipsBranchRelationshipsTrunkId1] -) AS [s0] ON [r].[Id] = [s0].[RelationshipsRootId] -LEFT JOIN ( - SELECT [r5].[RelationshipsTrunkRelationshipsRootId], [r5].[Id1], [r5].[Name], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchId1], [r6].[Id1] AS [Id10], [r6].[Name] AS [Name0], [r5].[OptionalReferenceLeaf_Name], [r5].[RequiredReferenceLeaf_Name] - FROM [Root_OptionalReferenceTrunk_CollectionBranch] AS [r5] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r6] ON [r5].[RelationshipsTrunkRelationshipsRootId] = [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r5].[Id1] = [r6].[RelationshipsBranchId1] -) AS [s1] ON CASE - WHEN [r].[OptionalReferenceTrunk_Name] IS NOT NULL THEN [r].[Id] -END = [s1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r7] ON CASE - WHEN [r].[OptionalReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r7].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r8] ON CASE - WHEN [r].[OptionalReferenceTrunk_RequiredReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r9].[RelationshipsTrunkRelationshipsRootId], [r9].[Id1], [r9].[Name], [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r10].[RelationshipsBranchId1], [r10].[Id1] AS [Id10], [r10].[Name] AS [Name0], [r9].[OptionalReferenceLeaf_Name], [r9].[RequiredReferenceLeaf_Name] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r9] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r10] ON [r9].[RelationshipsTrunkRelationshipsRootId] = [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r9].[Id1] = [r10].[RelationshipsBranchId1] -) AS [s2] ON [r].[Id] = [s2].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r11] ON CASE - WHEN [r].[RequiredReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r12] ON [r].[Id] = [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r13].[RelationshipsRootId], [r13].[Id1], [r13].[Name], [s3].[RelationshipsTrunkRelationshipsRootId], [s3].[RelationshipsTrunkId1], [s3].[Id1] AS [Id10], [s3].[Name] AS [Name0], [s3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s3].[RelationshipsBranchRelationshipsTrunkId1], [s3].[RelationshipsBranchId1], [s3].[Id10] AS [Id100], [s3].[Name0] AS [Name00], [s3].[OptionalReferenceLeaf_Name], [s3].[RequiredReferenceLeaf_Name], [r13].[OptionalReferenceBranch_Name], [r16].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r16].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r16].[Id1] AS [Id11], [r16].[Name] AS [Name1], [r13].[OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r13].[OptionalReferenceBranch_RequiredReferenceLeaf_Name], [r13].[RequiredReferenceBranch_Name], [r17].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r17].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r17].[Id1] AS [Id12], [r17].[Name] AS [Name2], [r13].[RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r13].[RequiredReferenceBranch_RequiredReferenceLeaf_Name] - FROM [Root_CollectionTrunk] AS [r13] - LEFT JOIN ( - SELECT [r14].[RelationshipsTrunkRelationshipsRootId], [r14].[RelationshipsTrunkId1], [r14].[Id1], [r14].[Name], [r15].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r15].[RelationshipsBranchRelationshipsTrunkId1], [r15].[RelationshipsBranchId1], [r15].[Id1] AS [Id10], [r15].[Name] AS [Name0], [r14].[OptionalReferenceLeaf_Name], [r14].[RequiredReferenceLeaf_Name] - FROM [Root_CollectionTrunk_CollectionBranch] AS [r14] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_CollectionLeaf] AS [r15] ON [r14].[RelationshipsTrunkRelationshipsRootId] = [r15].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r14].[RelationshipsTrunkId1] = [r15].[RelationshipsBranchRelationshipsTrunkId1] AND [r14].[Id1] = [r15].[RelationshipsBranchId1] - ) AS [s3] ON [r13].[RelationshipsRootId] = [s3].[RelationshipsTrunkRelationshipsRootId] AND [r13].[Id1] = [s3].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r16] ON CASE - WHEN [r13].[OptionalReferenceBranch_Name] IS NOT NULL THEN [r13].[RelationshipsRootId] - END = [r16].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND CASE - WHEN [r13].[OptionalReferenceBranch_Name] IS NOT NULL THEN [r13].[Id1] - END = [r16].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r17] ON [r13].[RelationshipsRootId] = [r17].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r13].[Id1] = [r17].[RelationshipsBranchRelationshipsTrunkId1] -) AS [s4] ON [r].[Id] = [s4].[RelationshipsRootId] -LEFT JOIN ( - SELECT [r18].[RelationshipsTrunkRelationshipsRootId], [r18].[Id1], [r18].[Name], [r19].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r19].[RelationshipsBranchId1], [r19].[Id1] AS [Id10], [r19].[Name] AS [Name0], [r18].[OptionalReferenceLeaf_Name], [r18].[RequiredReferenceLeaf_Name] - FROM [Root_OptionalReferenceTrunk_CollectionBranch] AS [r18] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r19] ON [r18].[RelationshipsTrunkRelationshipsRootId] = [r19].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r18].[Id1] = [r19].[RelationshipsBranchId1] -) AS [s5] ON CASE - WHEN [r].[OptionalReferenceTrunk_Name] IS NOT NULL THEN [r].[Id] -END = [s5].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r20] ON CASE - WHEN [r].[OptionalReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r20].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r21] ON CASE - WHEN [r].[OptionalReferenceTrunk_RequiredReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r21].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r22].[RelationshipsTrunkRelationshipsRootId], [r22].[Id1], [r22].[Name], [r23].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r23].[RelationshipsBranchId1], [r23].[Id1] AS [Id10], [r23].[Name] AS [Name0], [r22].[OptionalReferenceLeaf_Name], [r22].[RequiredReferenceLeaf_Name] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r22] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r23] ON [r22].[RelationshipsTrunkRelationshipsRootId] = [r23].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r22].[Id1] = [r23].[RelationshipsBranchId1] -) AS [s6] ON [r].[Id] = [s6].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r24] ON CASE - WHEN [r].[RequiredReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r24].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r25] ON [r].[Id] = [r25].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [s0].[RelationshipsRootId], [s0].[Id1], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsTrunkId1], [s0].[Id10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchRelationshipsTrunkId1], [s0].[RelationshipsBranchId1], [s0].[Id100], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchRelationshipsTrunkId10], [s0].[Id11], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchRelationshipsTrunkId11], [s0].[Id12], [s1].[RelationshipsTrunkRelationshipsRootId], [s1].[Id1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsBranchId1], [s1].[Id10], [r7].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r7].[Id1], [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r8].[Id1], [s2].[RelationshipsTrunkRelationshipsRootId], [s2].[Id1], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s2].[RelationshipsBranchId1], [s2].[Id10], [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[Id1], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r12].[Id1], [s4].[RelationshipsRootId], [s4].[Id1], [s4].[RelationshipsTrunkRelationshipsRootId], [s4].[RelationshipsTrunkId1], [s4].[Id10], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s4].[RelationshipsBranchRelationshipsTrunkId1], [s4].[RelationshipsBranchId1], [s4].[Id100], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s4].[RelationshipsBranchRelationshipsTrunkId10], [s4].[Id11], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s4].[RelationshipsBranchRelationshipsTrunkId11], [s4].[Id12], [s5].[RelationshipsTrunkRelationshipsRootId], [s5].[Id1], [s5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s5].[RelationshipsBranchId1], [s5].[Id10], [r20].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r20].[Id1], [r21].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r21].[Id1], [s6].[RelationshipsTrunkRelationshipsRootId], [s6].[Id1], [s6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s6].[RelationshipsBranchId1], [s6].[Id10], [r24].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r24].[Id1], [r25].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -"""); - } - - public override async Task Select_trunk_and_branch_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_trunk_and_branch_duplicated(async, queryTrackingBehavior); - - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r].[Id], [r].[OptionalReferenceTrunk_Name], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[Id10], [s].[Name0], [s].[OptionalReferenceLeaf_Name], [s].[RequiredReferenceLeaf_Name], [r].[OptionalReferenceTrunk_OptionalReferenceBranch_Name], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[Id1], [r2].[Name], [r].[OptionalReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r].[OptionalReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf_Name], [r].[OptionalReferenceTrunk_RequiredReferenceBranch_Name], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[Id1], [r3].[Name], [r].[OptionalReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r].[OptionalReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf_Name], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[Id1], [r4].[Name], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[Id1], [s0].[Name], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchId1], [s0].[Id10], [s0].[Name0], [s0].[OptionalReferenceLeaf_Name], [s0].[RequiredReferenceLeaf_Name], [r7].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r7].[Id1], [r7].[Name], [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r8].[Id1], [r8].[Name], [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r9].[Id1], [r9].[Name] -FROM [RootEntities] AS [r] -LEFT JOIN ( - SELECT [r0].[RelationshipsTrunkRelationshipsRootId], [r0].[Id1], [r0].[Name], [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r1].[RelationshipsBranchId1], [r1].[Id1] AS [Id10], [r1].[Name] AS [Name0], [r0].[OptionalReferenceLeaf_Name], [r0].[RequiredReferenceLeaf_Name] - FROM [Root_OptionalReferenceTrunk_CollectionBranch] AS [r0] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r1] ON [r0].[RelationshipsTrunkRelationshipsRootId] = [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [r1].[RelationshipsBranchId1] -) AS [s] ON CASE - WHEN [r].[OptionalReferenceTrunk_Name] IS NOT NULL THEN [r].[Id] -END = [s].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r2] ON CASE - WHEN [r].[OptionalReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r3] ON CASE - WHEN [r].[OptionalReferenceTrunk_RequiredReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r4] ON CASE - WHEN [r].[OptionalReferenceTrunk_RequiredReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r5].[RelationshipsTrunkRelationshipsRootId], [r5].[Id1], [r5].[Name], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchId1], [r6].[Id1] AS [Id10], [r6].[Name] AS [Name0], [r5].[OptionalReferenceLeaf_Name], [r5].[RequiredReferenceLeaf_Name] - FROM [Root_OptionalReferenceTrunk_CollectionBranch] AS [r5] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r6] ON [r5].[RelationshipsTrunkRelationshipsRootId] = [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r5].[Id1] = [r6].[RelationshipsBranchId1] -) AS [s0] ON CASE - WHEN [r].[OptionalReferenceTrunk_Name] IS NOT NULL THEN [r].[Id] -END = [s0].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r7] ON CASE - WHEN [r].[OptionalReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r7].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r8] ON CASE - WHEN [r].[OptionalReferenceTrunk_RequiredReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r9] ON CASE - WHEN [r].[OptionalReferenceTrunk_RequiredReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[Id10], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[Id1], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[Id1], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[Id1], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[Id1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchId1], [s0].[Id10], [r7].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r7].[Id1], [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r8].[Id1], [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] +SELECT [o0].[RelatedTypeRootEntityId], [o0].[Id], [o0].[Int], [o0].[Name], [o0].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated] AS [o] ON [r].[Id] = [o].[RootEntityId] +LEFT JOIN [OptionalRelated_RequiredNested] AS [o0] ON [o].[RootEntityId] = [o0].[RelatedTypeRootEntityId] """); } } - public override async Task Select_trunk_and_trunk_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_optional_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_trunk_and_trunk_duplicated(async, queryTrackingBehavior); + await base.Select_optional_related_optional_nested(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r].[Id], [r].[RequiredReferenceTrunk_Name], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[Id10], [s].[Name0], [s].[OptionalReferenceLeaf_Name], [s].[RequiredReferenceLeaf_Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_Name], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[Id1], [r2].[Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf_Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_Name], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[Id1], [r3].[Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf_Name], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[Id1], [s0].[Name], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchId1], [s0].[Id10], [s0].[Name0], [s0].[OptionalReferenceLeaf_Name], [s0].[RequiredReferenceLeaf_Name], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[Id1], [r6].[Name], [r7].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r7].[Id1], [r7].[Name] -FROM [RootEntities] AS [r] -LEFT JOIN ( - SELECT [r0].[RelationshipsTrunkRelationshipsRootId], [r0].[Id1], [r0].[Name], [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r1].[RelationshipsBranchId1], [r1].[Id1] AS [Id10], [r1].[Name] AS [Name0], [r0].[OptionalReferenceLeaf_Name], [r0].[RequiredReferenceLeaf_Name] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r0] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r1] ON [r0].[RelationshipsTrunkRelationshipsRootId] = [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [r1].[RelationshipsBranchId1] -) AS [s] ON [r].[Id] = [s].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r2] ON CASE - WHEN [r].[RequiredReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r3] ON [r].[Id] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r4].[RelationshipsTrunkRelationshipsRootId], [r4].[Id1], [r4].[Name], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsBranchId1], [r5].[Id1] AS [Id10], [r5].[Name] AS [Name0], [r4].[OptionalReferenceLeaf_Name], [r4].[RequiredReferenceLeaf_Name] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r4] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r5] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r4].[Id1] = [r5].[RelationshipsBranchId1] -) AS [s0] ON [r].[Id] = [s0].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r6] ON CASE - WHEN [r].[RequiredReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r7] ON [r].[Id] = [r7].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[Id10], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[Id1], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[Id1], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[Id1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchId1], [s0].[Id10], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[Id1], [r7].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] +SELECT [o0].[RelatedTypeRootEntityId], [o0].[Id], [o0].[Int], [o0].[Name], [o0].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated] AS [o] ON [r].[Id] = [o].[RootEntityId] +LEFT JOIN [OptionalRelated_OptionalNested] AS [o0] ON [o].[RootEntityId] = [o0].[RelatedTypeRootEntityId] """); } } - public override async Task Select_leaf_trunk_root(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_leaf_trunk_root(async, queryTrackingBehavior); + #endregion Non-collection - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r].[Id], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf_Name], [r].[RequiredReferenceTrunk_Name], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[Id10], [s].[Name0], [s].[OptionalReferenceLeaf_Name], [s].[RequiredReferenceLeaf_Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_Name], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[Id1], [r2].[Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r].[RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf_Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_Name], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[Id1], [r3].[Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [s1].[RelationshipsRootId], [s1].[Id1], [s1].[Name], [s1].[RelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsTrunkId1], [s1].[Id10], [s1].[Name0], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsBranchRelationshipsTrunkId1], [s1].[RelationshipsBranchId1], [s1].[Id100], [s1].[Name00], [s1].[OptionalReferenceLeaf_Name], [s1].[RequiredReferenceLeaf_Name], [s1].[OptionalReferenceBranch_Name], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s1].[RelationshipsBranchRelationshipsTrunkId10], [s1].[Id11], [s1].[Name1], [s1].[OptionalReferenceBranch_OptionalReferenceLeaf_Name], [s1].[OptionalReferenceBranch_RequiredReferenceLeaf_Name], [s1].[RequiredReferenceBranch_Name], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s1].[RelationshipsBranchRelationshipsTrunkId11], [s1].[Id12], [s1].[Name2], [s1].[RequiredReferenceBranch_OptionalReferenceLeaf_Name], [s1].[RequiredReferenceBranch_RequiredReferenceLeaf_Name], [r].[OptionalReferenceTrunk_Name], [s2].[RelationshipsTrunkRelationshipsRootId], [s2].[Id1], [s2].[Name], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s2].[RelationshipsBranchId1], [s2].[Id10], [s2].[Name0], [s2].[OptionalReferenceLeaf_Name], [s2].[RequiredReferenceLeaf_Name], [r].[OptionalReferenceTrunk_OptionalReferenceBranch_Name], [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[Id1], [r11].[Name], [r].[OptionalReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r].[OptionalReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf_Name], [r].[OptionalReferenceTrunk_RequiredReferenceBranch_Name], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r12].[Id1], [r12].[Name], [r].[OptionalReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r].[OptionalReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf_Name], [s3].[RelationshipsTrunkRelationshipsRootId], [s3].[Id1], [s3].[Name], [s3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s3].[RelationshipsBranchId1], [s3].[Id10], [s3].[Name0], [s3].[OptionalReferenceLeaf_Name], [s3].[RequiredReferenceLeaf_Name], [r15].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r15].[Id1], [r15].[Name], [r16].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r16].[Id1], [r16].[Name] -FROM [RootEntities] AS [r] -LEFT JOIN ( - SELECT [r0].[RelationshipsTrunkRelationshipsRootId], [r0].[Id1], [r0].[Name], [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r1].[RelationshipsBranchId1], [r1].[Id1] AS [Id10], [r1].[Name] AS [Name0], [r0].[OptionalReferenceLeaf_Name], [r0].[RequiredReferenceLeaf_Name] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r0] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r1] ON [r0].[RelationshipsTrunkRelationshipsRootId] = [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [r1].[RelationshipsBranchId1] -) AS [s] ON [r].[Id] = [s].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r2] ON CASE - WHEN [r].[RequiredReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r3] ON [r].[Id] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r4].[RelationshipsRootId], [r4].[Id1], [r4].[Name], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsTrunkId1], [s0].[Id1] AS [Id10], [s0].[Name] AS [Name0], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchRelationshipsTrunkId1], [s0].[RelationshipsBranchId1], [s0].[Id10] AS [Id100], [s0].[Name0] AS [Name00], [s0].[OptionalReferenceLeaf_Name], [s0].[RequiredReferenceLeaf_Name], [r4].[OptionalReferenceBranch_Name], [r7].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r7].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r7].[Id1] AS [Id11], [r7].[Name] AS [Name1], [r4].[OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r4].[OptionalReferenceBranch_RequiredReferenceLeaf_Name], [r4].[RequiredReferenceBranch_Name], [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r8].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r8].[Id1] AS [Id12], [r8].[Name] AS [Name2], [r4].[RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r4].[RequiredReferenceBranch_RequiredReferenceLeaf_Name] - FROM [Root_CollectionTrunk] AS [r4] - LEFT JOIN ( - SELECT [r5].[RelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsTrunkId1], [r5].[Id1], [r5].[Name], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchRelationshipsTrunkId1], [r6].[RelationshipsBranchId1], [r6].[Id1] AS [Id10], [r6].[Name] AS [Name0], [r5].[OptionalReferenceLeaf_Name], [r5].[RequiredReferenceLeaf_Name] - FROM [Root_CollectionTrunk_CollectionBranch] AS [r5] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_CollectionLeaf] AS [r6] ON [r5].[RelationshipsTrunkRelationshipsRootId] = [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r5].[RelationshipsTrunkId1] = [r6].[RelationshipsBranchRelationshipsTrunkId1] AND [r5].[Id1] = [r6].[RelationshipsBranchId1] - ) AS [s0] ON [r4].[RelationshipsRootId] = [s0].[RelationshipsTrunkRelationshipsRootId] AND [r4].[Id1] = [s0].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r7] ON CASE - WHEN [r4].[OptionalReferenceBranch_Name] IS NOT NULL THEN [r4].[RelationshipsRootId] - END = [r7].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND CASE - WHEN [r4].[OptionalReferenceBranch_Name] IS NOT NULL THEN [r4].[Id1] - END = [r7].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r8] ON [r4].[RelationshipsRootId] = [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r4].[Id1] = [r8].[RelationshipsBranchRelationshipsTrunkId1] -) AS [s1] ON [r].[Id] = [s1].[RelationshipsRootId] -LEFT JOIN ( - SELECT [r9].[RelationshipsTrunkRelationshipsRootId], [r9].[Id1], [r9].[Name], [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r10].[RelationshipsBranchId1], [r10].[Id1] AS [Id10], [r10].[Name] AS [Name0], [r9].[OptionalReferenceLeaf_Name], [r9].[RequiredReferenceLeaf_Name] - FROM [Root_OptionalReferenceTrunk_CollectionBranch] AS [r9] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r10] ON [r9].[RelationshipsTrunkRelationshipsRootId] = [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r9].[Id1] = [r10].[RelationshipsBranchId1] -) AS [s2] ON CASE - WHEN [r].[OptionalReferenceTrunk_Name] IS NOT NULL THEN [r].[Id] -END = [s2].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r11] ON CASE - WHEN [r].[OptionalReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r12] ON CASE - WHEN [r].[OptionalReferenceTrunk_RequiredReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r13].[RelationshipsTrunkRelationshipsRootId], [r13].[Id1], [r13].[Name], [r14].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r14].[RelationshipsBranchId1], [r14].[Id1] AS [Id10], [r14].[Name] AS [Name0], [r13].[OptionalReferenceLeaf_Name], [r13].[RequiredReferenceLeaf_Name] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r13] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r14] ON [r13].[RelationshipsTrunkRelationshipsRootId] = [r14].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r13].[Id1] = [r14].[RelationshipsBranchId1] -) AS [s3] ON [r].[Id] = [s3].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r15] ON CASE - WHEN [r].[RequiredReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r].[Id] -END = [r15].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r16] ON [r].[Id] = [r16].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[Id10], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[Id1], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[Id1], [s1].[RelationshipsRootId], [s1].[Id1], [s1].[RelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsTrunkId1], [s1].[Id10], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsBranchRelationshipsTrunkId1], [s1].[RelationshipsBranchId1], [s1].[Id100], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s1].[RelationshipsBranchRelationshipsTrunkId10], [s1].[Id11], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s1].[RelationshipsBranchRelationshipsTrunkId11], [s1].[Id12], [s2].[RelationshipsTrunkRelationshipsRootId], [s2].[Id1], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s2].[RelationshipsBranchId1], [s2].[Id10], [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[Id1], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r12].[Id1], [s3].[RelationshipsTrunkRelationshipsRootId], [s3].[Id1], [s3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s3].[RelationshipsBranchId1], [s3].[Id10], [r15].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r15].[Id1], [r16].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -"""); - } - } + #region Collection - public override async Task Select_multiple_branch_leaf(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_multiple_branch_leaf(async, queryTrackingBehavior); + await base.Select_related_collection(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r].[Id], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_Name], [r0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r0].[Id1], [r0].[Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf_Name], [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r1].[Id1], [r1].[Name], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[Id10], [s].[Name0], [s].[OptionalReferenceLeaf_Name], [s].[RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r0] ON [r].[Id] = [r0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r1] ON [r].[Id] = [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] +SELECT [r].[Id], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [s].[Int0], [s].[Name0], [s].[String0], [s].[Id1], [s].[Int1], [s].[Name1], [s].[String1], [s].[Id2], [s].[Int2], [s].[Name2], [s].[String2] +FROM [RootEntity] AS [r] LEFT JOIN ( - SELECT [r2].[RelationshipsTrunkRelationshipsRootId], [r2].[Id1], [r2].[Name], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchId1], [r3].[Id1] AS [Id10], [r3].[Name] AS [Name0], [r2].[OptionalReferenceLeaf_Name], [r2].[RequiredReferenceLeaf_Name] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r2] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r3] ON [r2].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r2].[Id1] = [r3].[RelationshipsBranchId1] -) AS [s] ON [r].[Id] = [s].[RelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r0].[Id1], [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r1].[Id1], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1] + SELECT [r0].[RootEntityId], [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String], [r1].[RelatedTypeRootEntityId], [r1].[RelatedTypeId], [r2].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId0], [r2].[RelatedTypeId] AS [RelatedTypeId0], [r3].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId1], [r3].[RelatedTypeId] AS [RelatedTypeId1], [r3].[Id] AS [Id0], [r3].[Int] AS [Int0], [r3].[Name] AS [Name0], [r3].[String] AS [String0], [r1].[Id] AS [Id1], [r1].[Int] AS [Int1], [r1].[Name] AS [Name1], [r1].[String] AS [String1], [r2].[Id] AS [Id2], [r2].[Int] AS [Int2], [r2].[Name] AS [Name2], [r2].[String] AS [String2] + FROM [RelatedCollection] AS [r0] + LEFT JOIN [RelatedCollection_OptionalNested] AS [r1] ON [r0].[RootEntityId] = [r1].[RelatedTypeRootEntityId] AND [r0].[Id] = [r1].[RelatedTypeId] + LEFT JOIN [RelatedCollection_RequiredNested] AS [r2] ON [r0].[RootEntityId] = [r2].[RelatedTypeRootEntityId] AND [r0].[Id] = [r2].[RelatedTypeId] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r3] ON [r0].[RootEntityId] = [r3].[RelatedTypeRootEntityId] AND [r0].[Id] = [r3].[RelatedTypeId] +) AS [s] ON [r].[Id] = [s].[RootEntityId] +ORDER BY [r].[Id], [s].[RootEntityId], [s].[Id], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1] """); } } - #endregion Multiple - - #region Subquery - - public override async Task Select_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_subquery_root_set_required_trunk_FirstOrDefault_branch(async, queryTrackingBehavior); + await base.Select_required_related_nested_collection(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r2].[Id], [r2].[RequiredReferenceTrunk_RequiredReferenceBranch_Name], [r].[Id], [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r1].[Id1], [r1].[Name], [r2].[RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r2].[RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -OUTER APPLY ( - SELECT TOP(1) [r0].[Id], [r0].[RequiredReferenceTrunk_RequiredReferenceBranch_Name], [r0].[RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r0].[RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf_Name] - FROM [RootEntities] AS [r0] - ORDER BY [r0].[Id] -) AS [r2] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r1] ON [r2].[Id] = [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r2].[Id], [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] +SELECT [r].[Id], [r0].[RootEntityId], [r1].[RelatedTypeRootEntityId], [r1].[Id], [r1].[Int], [r1].[Name], [r1].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RequiredRelated] AS [r0] ON [r].[Id] = [r0].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r1] ON [r0].[RootEntityId] = [r1].[RelatedTypeRootEntityId] +ORDER BY [r].[Id], [r0].[RootEntityId], [r1].[RelatedTypeRootEntityId] """); } } - public override async Task Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(async, queryTrackingBehavior); + await base.Select_optional_related_nested_collection(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r2].[Id], [r2].[OptionalReferenceTrunk_OptionalReferenceBranch_Name], [r].[Id], [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r1].[Id1], [r1].[Name], [r2].[OptionalReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r2].[OptionalReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -OUTER APPLY ( - SELECT TOP(1) [r0].[Id], [r0].[OptionalReferenceTrunk_OptionalReferenceBranch_Name], [r0].[OptionalReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r0].[OptionalReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf_Name] - FROM [RootEntities] AS [r0] - ORDER BY [r0].[Id] -) AS [r2] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r1] ON CASE - WHEN [r2].[OptionalReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r2].[Id] -END = [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r2].[Id], [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] +SELECT [r].[Id], [o].[RootEntityId], [o0].[RelatedTypeRootEntityId], [o0].[Id], [o0].[Int], [o0].[Name], [o0].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated] AS [o] ON [r].[Id] = [o].[RootEntityId] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o0] ON [o].[RootEntityId] = [o0].[RelatedTypeRootEntityId] +ORDER BY [r].[Id], [o].[RootEntityId], [o0].[RelatedTypeRootEntityId] """); } } - public override async Task Select_subquery_root_set_trunk_FirstOrDefault_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task SelectMany_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_subquery_root_set_trunk_FirstOrDefault_collection(async, queryTrackingBehavior); + await base.SelectMany_related_collection(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r].[Id], [r3].[Id], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[Id10], [s].[Name0], [s].[OptionalReferenceLeaf_Name], [s].[RequiredReferenceLeaf_Name], [r3].[c] -FROM [RootEntities] AS [r] -OUTER APPLY ( - SELECT TOP(1) 1 AS [c], [r0].[Id] - FROM [RootEntities] AS [r0] - ORDER BY [r0].[Id] -) AS [r3] -LEFT JOIN ( - SELECT [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[Id1], [r1].[Name], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchId1], [r2].[Id1] AS [Id10], [r2].[Name] AS [Name0], [r1].[OptionalReferenceLeaf_Name], [r1].[RequiredReferenceLeaf_Name] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[Id1] = [r2].[RelationshipsBranchId1] -) AS [s] ON [r3].[Id] = [s].[RelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r3].[Id], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1] +SELECT [r0].[RootEntityId], [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String], [r].[Id], [r1].[RelatedTypeRootEntityId], [r1].[RelatedTypeId], [r2].[RelatedTypeRootEntityId], [r2].[RelatedTypeId], [r3].[RelatedTypeRootEntityId], [r3].[RelatedTypeId], [r3].[Id], [r3].[Int], [r3].[Name], [r3].[String], [r1].[Id], [r1].[Int], [r1].[Name], [r1].[String], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String] +FROM [RootEntity] AS [r] +INNER JOIN [RelatedCollection] AS [r0] ON [r].[Id] = [r0].[RootEntityId] +LEFT JOIN [RelatedCollection_OptionalNested] AS [r1] ON [r0].[RootEntityId] = [r1].[RelatedTypeRootEntityId] AND [r0].[Id] = [r1].[RelatedTypeId] +LEFT JOIN [RelatedCollection_RequiredNested] AS [r2] ON [r0].[RootEntityId] = [r2].[RelatedTypeRootEntityId] AND [r0].[Id] = [r2].[RelatedTypeId] +LEFT JOIN [RelatedCollection_NestedCollection] AS [r3] ON [r0].[RootEntityId] = [r3].[RelatedTypeRootEntityId] AND [r0].[Id] = [r3].[RelatedTypeId] +ORDER BY [r].[Id], [r0].[RootEntityId], [r0].[Id], [r1].[RelatedTypeRootEntityId], [r1].[RelatedTypeId], [r2].[RelatedTypeRootEntityId], [r2].[RelatedTypeId], [r3].[RelatedTypeRootEntityId], [r3].[RelatedTypeId] """); } } - public override async Task Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task SelectMany_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(async, queryTrackingBehavior); + await base.SelectMany_required_related_nested_collection(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r].[Id], [r8].[Id], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[Id10], [s].[Name0], [s].[OptionalReferenceLeaf_Name], [s].[RequiredReferenceLeaf_Name], [r8].[RequiredReferenceTrunk_Name], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[Id1], [s0].[Name], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchId1], [s0].[Id10], [s0].[Name0], [s0].[OptionalReferenceLeaf_Name], [s0].[RequiredReferenceLeaf_Name], [r8].[RequiredReferenceTrunk_OptionalReferenceBranch_Name], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r5].[Id1], [r5].[Name], [r8].[RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r8].[RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf_Name], [r8].[RequiredReferenceTrunk_RequiredReferenceBranch_Name], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[Id1], [r6].[Name], [r8].[RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r8].[RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf_Name], [r7].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r7].[Id1], [r7].[Name], [r8].[RequiredReferenceTrunk_RequiredReferenceBranch_Name0], [r8].[c] -FROM [RootEntities] AS [r] -OUTER APPLY ( - SELECT TOP(1) [r0].[Id], [r0].[RequiredReferenceTrunk_Name], [r0].[RequiredReferenceTrunk_OptionalReferenceBranch_Name], [r0].[RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r0].[RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf_Name], [r0].[RequiredReferenceTrunk_RequiredReferenceBranch_Name], [r0].[RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r0].[RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf_Name], [r].[RequiredReferenceTrunk_RequiredReferenceBranch_Name] AS [RequiredReferenceTrunk_RequiredReferenceBranch_Name0], 1 AS [c] - FROM [RootEntities] AS [r0] - ORDER BY [r0].[Id] -) AS [r8] -LEFT JOIN ( - SELECT [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[Id1], [r1].[Name], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchId1], [r2].[Id1] AS [Id10], [r2].[Name] AS [Name0], [r1].[OptionalReferenceLeaf_Name], [r1].[RequiredReferenceLeaf_Name] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[Id1] = [r2].[RelationshipsBranchId1] -) AS [s] ON [r].[Id] = [s].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r3].[RelationshipsTrunkRelationshipsRootId], [r3].[Id1], [r3].[Name], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsBranchId1], [r4].[Id1] AS [Id10], [r4].[Name] AS [Name0], [r3].[OptionalReferenceLeaf_Name], [r3].[RequiredReferenceLeaf_Name] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r3] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r4] ON [r3].[RelationshipsTrunkRelationshipsRootId] = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r3].[Id1] = [r4].[RelationshipsBranchId1] -) AS [s0] ON [r8].[Id] = [s0].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r5] ON CASE - WHEN [r8].[RequiredReferenceTrunk_OptionalReferenceBranch_Name] IS NOT NULL THEN [r8].[Id] -END = [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r6] ON [r8].[Id] = [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r7] ON [r8].[Id] = [r7].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r8].[Id], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[Id10], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[Id1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchId1], [s0].[Id10], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r5].[Id1], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[Id1], [r7].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] +SELECT [r1].[RelatedTypeRootEntityId], [r1].[Id], [r1].[Int], [r1].[Name], [r1].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RequiredRelated] AS [r0] ON [r].[Id] = [r0].[RootEntityId] +INNER JOIN [RequiredRelated_NestedCollection] AS [r1] ON [r0].[RootEntityId] = [r1].[RelatedTypeRootEntityId] """); } } - public override async Task Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task SelectMany_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(async, queryTrackingBehavior); + await base.SelectMany_optional_related_nested_collection(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r].[Id], [r3].[Id], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[Id10], [s].[Name0], [s].[OptionalReferenceLeaf_Name], [s].[RequiredReferenceLeaf_Name], [r3].[c] -FROM [RootEntities] AS [r] -OUTER APPLY ( - SELECT TOP(1) 1 AS [c], [r0].[Id] - FROM [RootEntities] AS [r0] - ORDER BY [r0].[Id] -) AS [r3] -LEFT JOIN ( - SELECT [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[Id1], [r1].[Name], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchId1], [r2].[Id1] AS [Id10], [r2].[Name] AS [Name0], [r1].[OptionalReferenceLeaf_Name], [r1].[RequiredReferenceLeaf_Name] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[Id1] = [r2].[RelationshipsBranchId1] -) AS [s] ON [r].[Id] = [s].[RelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r3].[Id], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1] +SELECT [o0].[RelatedTypeRootEntityId], [o0].[Id], [o0].[Int], [o0].[Name], [o0].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated] AS [o] ON [r].[Id] = [o].[RootEntityId] +INNER JOIN [OptionalRelated_NestedCollection] AS [o0] ON [o].[RootEntityId] = [o0].[RelatedTypeRootEntityId] """); } } - #endregion Subquery + #endregion Collection - #region SelectMany + #region Multiple - public override async Task SelectMany_trunk_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_root_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.SelectMany_trunk_collection(async, queryTrackingBehavior); + await base.Select_root_duplicated(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r0].[RelationshipsRootId], [r0].[Id1], [r0].[Name], [r].[Id], [s].[RelationshipsTrunkRelationshipsRootId], [s].[RelationshipsTrunkId1], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchRelationshipsTrunkId1], [s].[RelationshipsBranchId1], [s].[Id10], [s].[Name0], [s].[OptionalReferenceLeaf_Name], [s].[RequiredReferenceLeaf_Name], [r0].[OptionalReferenceBranch_Name], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkId1], [r3].[Id1], [r3].[Name], [r0].[OptionalReferenceBranch_OptionalReferenceLeaf_Name], [r0].[OptionalReferenceBranch_RequiredReferenceLeaf_Name], [r0].[RequiredReferenceBranch_Name], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsBranchRelationshipsTrunkId1], [r4].[Id1], [r4].[Name], [r0].[RequiredReferenceBranch_OptionalReferenceLeaf_Name], [r0].[RequiredReferenceBranch_RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -INNER JOIN [Root_CollectionTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [o].[RootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r0].[RootEntityId], [r1].[RelatedTypeRootEntityId], [r2].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [o2].[Int], [o2].[Name], [o2].[String], [o0].[Id], [o0].[Int], [o0].[Name], [o0].[String], [o1].[Id], [o1].[Int], [o1].[Name], [o1].[String], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [s].[Int0], [s].[Name0], [s].[String0], [s].[Id1], [s].[Int1], [s].[Name1], [s].[String1], [s].[Id2], [s].[Int2], [s].[Name2], [s].[String2], [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String], [r7].[RelatedTypeRootEntityId], [r7].[Id], [r7].[Int], [r7].[Name], [r7].[String], [r1].[Id], [r1].[Int], [r1].[Name], [r1].[String], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String], [o3].[RelatedTypeRootEntityId], [o3].[Id], [o3].[Int], [o3].[Name], [o3].[String], [s0].[RootEntityId], [s0].[Id], [s0].[Int], [s0].[Name], [s0].[String], [s0].[RelatedTypeRootEntityId], [s0].[RelatedTypeId], [s0].[RelatedTypeRootEntityId0], [s0].[RelatedTypeId0], [s0].[RelatedTypeRootEntityId1], [s0].[RelatedTypeId1], [s0].[Id0], [s0].[Int0], [s0].[Name0], [s0].[String0], [s0].[Id1], [s0].[Int1], [s0].[Name1], [s0].[String1], [s0].[Id2], [s0].[Int2], [s0].[Name2], [s0].[String2], [r12].[RelatedTypeRootEntityId], [r12].[Id], [r12].[Int], [r12].[Name], [r12].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated] AS [o] ON [r].[Id] = [o].[RootEntityId] +LEFT JOIN [OptionalRelated_OptionalNested] AS [o0] ON [o].[RootEntityId] = [o0].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_RequiredNested] AS [o1] ON [o].[RootEntityId] = [o1].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated] AS [r0] ON [r].[Id] = [r0].[RootEntityId] +LEFT JOIN [RequiredRelated_OptionalNested] AS [r1] ON [r0].[RootEntityId] = [r1].[RelatedTypeRootEntityId] +LEFT JOIN [RequiredRelated_RequiredNested] AS [r2] ON [r0].[RootEntityId] = [r2].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o2] ON [o].[RootEntityId] = [o2].[RelatedTypeRootEntityId] LEFT JOIN ( - SELECT [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[RelationshipsTrunkId1], [r1].[Id1], [r1].[Name], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkId1], [r2].[RelationshipsBranchId1], [r2].[Id1] AS [Id10], [r2].[Name] AS [Name0], [r1].[OptionalReferenceLeaf_Name], [r1].[RequiredReferenceLeaf_Name] - FROM [Root_CollectionTrunk_CollectionBranch] AS [r1] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_CollectionLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[RelationshipsTrunkId1] = [r2].[RelationshipsBranchRelationshipsTrunkId1] AND [r1].[Id1] = [r2].[RelationshipsBranchId1] -) AS [s] ON [r0].[RelationshipsRootId] = [s].[RelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [s].[RelationshipsTrunkId1] -LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r3] ON CASE - WHEN [r0].[OptionalReferenceBranch_Name] IS NOT NULL THEN [r0].[RelationshipsRootId] -END = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND CASE - WHEN [r0].[OptionalReferenceBranch_Name] IS NOT NULL THEN [r0].[Id1] -END = [r3].[RelationshipsBranchRelationshipsTrunkId1] -LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r4] ON [r0].[RelationshipsRootId] = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [r4].[RelationshipsBranchRelationshipsTrunkId1] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [r0].[Id1], [s].[RelationshipsTrunkRelationshipsRootId], [s].[RelationshipsTrunkId1], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchRelationshipsTrunkId1], [s].[RelationshipsBranchId1], [s].[Id10], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkId1], [r3].[Id1], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsBranchRelationshipsTrunkId1] + SELECT [r3].[RootEntityId], [r3].[Id], [r3].[Int], [r3].[Name], [r3].[String], [r4].[RelatedTypeRootEntityId], [r4].[RelatedTypeId], [r5].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId0], [r5].[RelatedTypeId] AS [RelatedTypeId0], [r6].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId1], [r6].[RelatedTypeId] AS [RelatedTypeId1], [r6].[Id] AS [Id0], [r6].[Int] AS [Int0], [r6].[Name] AS [Name0], [r6].[String] AS [String0], [r4].[Id] AS [Id1], [r4].[Int] AS [Int1], [r4].[Name] AS [Name1], [r4].[String] AS [String1], [r5].[Id] AS [Id2], [r5].[Int] AS [Int2], [r5].[Name] AS [Name2], [r5].[String] AS [String2] + FROM [RelatedCollection] AS [r3] + LEFT JOIN [RelatedCollection_OptionalNested] AS [r4] ON [r3].[RootEntityId] = [r4].[RelatedTypeRootEntityId] AND [r3].[Id] = [r4].[RelatedTypeId] + LEFT JOIN [RelatedCollection_RequiredNested] AS [r5] ON [r3].[RootEntityId] = [r5].[RelatedTypeRootEntityId] AND [r3].[Id] = [r5].[RelatedTypeId] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r6] ON [r3].[RootEntityId] = [r6].[RelatedTypeRootEntityId] AND [r3].[Id] = [r6].[RelatedTypeId] +) AS [s] ON [r].[Id] = [s].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r7] ON [r0].[RootEntityId] = [r7].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o3] ON [o].[RootEntityId] = [o3].[RelatedTypeRootEntityId] +LEFT JOIN ( + SELECT [r8].[RootEntityId], [r8].[Id], [r8].[Int], [r8].[Name], [r8].[String], [r9].[RelatedTypeRootEntityId], [r9].[RelatedTypeId], [r10].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId0], [r10].[RelatedTypeId] AS [RelatedTypeId0], [r11].[RelatedTypeRootEntityId] AS [RelatedTypeRootEntityId1], [r11].[RelatedTypeId] AS [RelatedTypeId1], [r11].[Id] AS [Id0], [r11].[Int] AS [Int0], [r11].[Name] AS [Name0], [r11].[String] AS [String0], [r9].[Id] AS [Id1], [r9].[Int] AS [Int1], [r9].[Name] AS [Name1], [r9].[String] AS [String1], [r10].[Id] AS [Id2], [r10].[Int] AS [Int2], [r10].[Name] AS [Name2], [r10].[String] AS [String2] + FROM [RelatedCollection] AS [r8] + LEFT JOIN [RelatedCollection_OptionalNested] AS [r9] ON [r8].[RootEntityId] = [r9].[RelatedTypeRootEntityId] AND [r8].[Id] = [r9].[RelatedTypeId] + LEFT JOIN [RelatedCollection_RequiredNested] AS [r10] ON [r8].[RootEntityId] = [r10].[RelatedTypeRootEntityId] AND [r8].[Id] = [r10].[RelatedTypeId] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r11] ON [r8].[RootEntityId] = [r11].[RelatedTypeRootEntityId] AND [r8].[Id] = [r11].[RelatedTypeId] +) AS [s0] ON [r].[Id] = [s0].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r12] ON [r0].[RootEntityId] = [r12].[RelatedTypeRootEntityId] +ORDER BY [r].[Id], [o].[RootEntityId], [o0].[RelatedTypeRootEntityId], [o1].[RelatedTypeRootEntityId], [r0].[RootEntityId], [r1].[RelatedTypeRootEntityId], [r2].[RelatedTypeRootEntityId], [o2].[RelatedTypeRootEntityId], [o2].[Id], [s].[RootEntityId], [s].[Id], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[RelatedTypeRootEntityId0], [s].[RelatedTypeId0], [s].[RelatedTypeRootEntityId1], [s].[RelatedTypeId1], [s].[Id0], [r7].[RelatedTypeRootEntityId], [r7].[Id], [o3].[RelatedTypeRootEntityId], [o3].[Id], [s0].[RootEntityId], [s0].[Id], [s0].[RelatedTypeRootEntityId], [s0].[RelatedTypeId], [s0].[RelatedTypeRootEntityId0], [s0].[RelatedTypeId0], [s0].[RelatedTypeRootEntityId1], [s0].[RelatedTypeId1], [s0].[Id0], [r12].[RelatedTypeRootEntityId] """); - } } - public override async Task SelectMany_required_trunk_reference_branch_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + #endregion Multiple + + #region Subquery + + public override async Task Select_subquery_required_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.SelectMany_required_trunk_reference_branch_collection(async, queryTrackingBehavior); + await base.Select_subquery_required_related_FirstOrDefault(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r0].[RelationshipsTrunkRelationshipsRootId], [r0].[Id1], [r0].[Name], [r].[Id], [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r1].[RelationshipsBranchId1], [r1].[Id1], [r1].[Name], [r0].[OptionalReferenceLeaf_Name], [r0].[RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -INNER JOIN [Root_RequiredReferenceTrunk_CollectionBranch] AS [r0] ON [r].[Id] = [r0].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r1] ON [r0].[RelationshipsTrunkRelationshipsRootId] = [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [r1].[RelationshipsBranchId1] -ORDER BY [r].[Id], [r0].[RelationshipsTrunkRelationshipsRootId], [r0].[Id1], [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r1].[RelationshipsBranchId1] +SELECT [s].[RelatedTypeRootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String] +FROM [RootEntity] AS [r] +OUTER APPLY ( + SELECT TOP(1) [r2].[RelatedTypeRootEntityId], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String] + FROM [RootEntity] AS [r0] + LEFT JOIN [RequiredRelated] AS [r1] ON [r0].[Id] = [r1].[RootEntityId] + LEFT JOIN [RequiredRelated_RequiredNested] AS [r2] ON [r1].[RootEntityId] = [r2].[RelatedTypeRootEntityId] + ORDER BY [r0].[Id] +) AS [s] """); } } - public override async Task SelectMany_optional_trunk_reference_branch_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_subquery_optional_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.SelectMany_optional_trunk_reference_branch_collection(async, queryTrackingBehavior); + await base.Select_subquery_optional_related_FirstOrDefault(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r0].[RelationshipsTrunkRelationshipsRootId], [r0].[Id1], [r0].[Name], [r].[Id], [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r1].[RelationshipsBranchId1], [r1].[Id1], [r1].[Name], [r0].[OptionalReferenceLeaf_Name], [r0].[RequiredReferenceLeaf_Name] -FROM [RootEntities] AS [r] -INNER JOIN [Root_OptionalReferenceTrunk_CollectionBranch] AS [r0] ON CASE - WHEN [r].[OptionalReferenceTrunk_Name] IS NOT NULL THEN [r].[Id] -END = [r0].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r1] ON [r0].[RelationshipsTrunkRelationshipsRootId] = [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [r1].[RelationshipsBranchId1] -ORDER BY [r].[Id], [r0].[RelationshipsTrunkRelationshipsRootId], [r0].[Id1], [r1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r1].[RelationshipsBranchId1] +SELECT [s].[RelatedTypeRootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String] +FROM [RootEntity] AS [r] +OUTER APPLY ( + SELECT TOP(1) [o0].[RelatedTypeRootEntityId], [o0].[Id], [o0].[Int], [o0].[Name], [o0].[String] + FROM [RootEntity] AS [r0] + LEFT JOIN [OptionalRelated] AS [o] ON [r0].[Id] = [o].[RootEntityId] + LEFT JOIN [OptionalRelated_RequiredNested] AS [o0] ON [o].[RootEntityId] = [o0].[RelatedTypeRootEntityId] + ORDER BY [r0].[Id] +) AS [s] """); } } - #endregion SelectMany + #endregion Subquery [ConditionalFact] public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); - - private void AssertSql(params string[] expected) - => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsSqlServerFixture.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsSqlServerFixture.cs index 5c2454db0ff..3f6631ff256 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsSqlServerFixture.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsSqlServerFixture.cs @@ -3,11 +3,8 @@ namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; -public class OwnedNavigationsSqlServerFixture : OwnedNavigationsRelationalFixtureBase, ITestSqlLoggerFactory +public class OwnedNavigationsSqlServerFixture : OwnedNavigationsRelationalFixtureBase { protected override ITestStoreFactory TestStoreFactory => SqlServerTestStoreFactory.Instance; - - public TestSqlLoggerFactory TestSqlLoggerFactory - => (TestSqlLoggerFactory)ListLoggerFactory; } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingMiscellaneousSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingMiscellaneousSqlServerTest.cs new file mode 100644 index 00000000000..108828ddde3 --- /dev/null +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingMiscellaneousSqlServerTest.cs @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedTableSplitting; + +public class OwnedTableSplittingMiscellaneousSqlServerTest( + OwnedTableSplittingSqlServerFixture fixture, + ITestOutputHelper testOutputHelper) + : OwnedTableSplittingMiscellaneousRelationalTestBase(fixture, testOutputHelper) +{ + #region Simple filters + + public override async Task Where_related_property(bool async) + { + await base.Where_related_property(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated_Id], [r].[OptionalRelated_Int], [r].[OptionalRelated_Name], [r].[OptionalRelated_String], [o].[RelatedTypeRootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String], [r].[OptionalRelated_OptionalNested_Id], [r].[OptionalRelated_OptionalNested_Int], [r].[OptionalRelated_OptionalNested_Name], [r].[OptionalRelated_OptionalNested_String], [r].[OptionalRelated_RequiredNested_Id], [r].[OptionalRelated_RequiredNested_Int], [r].[OptionalRelated_RequiredNested_Name], [r].[OptionalRelated_RequiredNested_String], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[Id0], [s].[Int0], [s].[Name0], [s].[String0], [s].[OptionalNested_Id], [s].[OptionalNested_Int], [s].[OptionalNested_Name], [s].[OptionalNested_String], [s].[RequiredNested_Id], [s].[RequiredNested_Int], [s].[RequiredNested_Name], [s].[RequiredNested_String], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r2].[RelatedTypeRootEntityId], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String], [r].[RequiredRelated_OptionalNested_Id], [r].[RequiredRelated_OptionalNested_Int], [r].[RequiredRelated_OptionalNested_Name], [r].[RequiredRelated_OptionalNested_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o] ON CASE + WHEN [r].[OptionalRelated_Id] IS NOT NULL AND [r].[OptionalRelated_Int] IS NOT NULL AND [r].[OptionalRelated_Name] IS NOT NULL AND [r].[OptionalRelated_String] IS NOT NULL THEN [r].[Id] +END = [o].[RelatedTypeRootEntityId] +LEFT JOIN ( + SELECT [r0].[RootEntityId], [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String], [r1].[RelatedTypeRootEntityId], [r1].[RelatedTypeId], [r1].[Id] AS [Id0], [r1].[Int] AS [Int0], [r1].[Name] AS [Name0], [r1].[String] AS [String0], [r0].[OptionalNested_Id], [r0].[OptionalNested_Int], [r0].[OptionalNested_Name], [r0].[OptionalNested_String], [r0].[RequiredNested_Id], [r0].[RequiredNested_Int], [r0].[RequiredNested_Name], [r0].[RequiredNested_String] + FROM [RelatedCollection] AS [r0] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r1] ON [r0].[RootEntityId] = [r1].[RelatedTypeRootEntityId] AND [r0].[Id] = [r1].[RelatedTypeId] +) AS [s] ON [r].[Id] = [s].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r2] ON [r].[Id] = [r2].[RelatedTypeRootEntityId] +WHERE [r].[RequiredRelated_Int] = 8 +ORDER BY [r].[Id], [o].[RelatedTypeRootEntityId], [o].[Id], [s].[RootEntityId], [s].[Id], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[Id0], [r2].[RelatedTypeRootEntityId] +"""); + } + + public override async Task Where_optional_related_property(bool async) + { + await base.Where_optional_related_property(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated_Id], [r].[OptionalRelated_Int], [r].[OptionalRelated_Name], [r].[OptionalRelated_String], [o].[RelatedTypeRootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String], [r].[OptionalRelated_OptionalNested_Id], [r].[OptionalRelated_OptionalNested_Int], [r].[OptionalRelated_OptionalNested_Name], [r].[OptionalRelated_OptionalNested_String], [r].[OptionalRelated_RequiredNested_Id], [r].[OptionalRelated_RequiredNested_Int], [r].[OptionalRelated_RequiredNested_Name], [r].[OptionalRelated_RequiredNested_String], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[Id0], [s].[Int0], [s].[Name0], [s].[String0], [s].[OptionalNested_Id], [s].[OptionalNested_Int], [s].[OptionalNested_Name], [s].[OptionalNested_String], [s].[RequiredNested_Id], [s].[RequiredNested_Int], [s].[RequiredNested_Name], [s].[RequiredNested_String], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r2].[RelatedTypeRootEntityId], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String], [r].[RequiredRelated_OptionalNested_Id], [r].[RequiredRelated_OptionalNested_Int], [r].[RequiredRelated_OptionalNested_Name], [r].[RequiredRelated_OptionalNested_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o] ON CASE + WHEN [r].[OptionalRelated_Id] IS NOT NULL AND [r].[OptionalRelated_Int] IS NOT NULL AND [r].[OptionalRelated_Name] IS NOT NULL AND [r].[OptionalRelated_String] IS NOT NULL THEN [r].[Id] +END = [o].[RelatedTypeRootEntityId] +LEFT JOIN ( + SELECT [r0].[RootEntityId], [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String], [r1].[RelatedTypeRootEntityId], [r1].[RelatedTypeId], [r1].[Id] AS [Id0], [r1].[Int] AS [Int0], [r1].[Name] AS [Name0], [r1].[String] AS [String0], [r0].[OptionalNested_Id], [r0].[OptionalNested_Int], [r0].[OptionalNested_Name], [r0].[OptionalNested_String], [r0].[RequiredNested_Id], [r0].[RequiredNested_Int], [r0].[RequiredNested_Name], [r0].[RequiredNested_String] + FROM [RelatedCollection] AS [r0] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r1] ON [r0].[RootEntityId] = [r1].[RelatedTypeRootEntityId] AND [r0].[Id] = [r1].[RelatedTypeId] +) AS [s] ON [r].[Id] = [s].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r2] ON [r].[Id] = [r2].[RelatedTypeRootEntityId] +WHERE [r].[OptionalRelated_Int] = 9 +ORDER BY [r].[Id], [o].[RelatedTypeRootEntityId], [o].[Id], [s].[RootEntityId], [s].[Id], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[Id0], [r2].[RelatedTypeRootEntityId] +"""); + } + + public override async Task Where_nested_related_property(bool async) + { + await base.Where_nested_related_property(async); + + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated_Id], [r].[OptionalRelated_Int], [r].[OptionalRelated_Name], [r].[OptionalRelated_String], [o].[RelatedTypeRootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String], [r].[OptionalRelated_OptionalNested_Id], [r].[OptionalRelated_OptionalNested_Int], [r].[OptionalRelated_OptionalNested_Name], [r].[OptionalRelated_OptionalNested_String], [r].[OptionalRelated_RequiredNested_Id], [r].[OptionalRelated_RequiredNested_Int], [r].[OptionalRelated_RequiredNested_Name], [r].[OptionalRelated_RequiredNested_String], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[Id0], [s].[Int0], [s].[Name0], [s].[String0], [s].[OptionalNested_Id], [s].[OptionalNested_Int], [s].[OptionalNested_Name], [s].[OptionalNested_String], [s].[RequiredNested_Id], [s].[RequiredNested_Int], [s].[RequiredNested_Name], [s].[RequiredNested_String], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r2].[RelatedTypeRootEntityId], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String], [r].[RequiredRelated_OptionalNested_Id], [r].[RequiredRelated_OptionalNested_Int], [r].[RequiredRelated_OptionalNested_Name], [r].[RequiredRelated_OptionalNested_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o] ON CASE + WHEN [r].[OptionalRelated_Id] IS NOT NULL AND [r].[OptionalRelated_Int] IS NOT NULL AND [r].[OptionalRelated_Name] IS NOT NULL AND [r].[OptionalRelated_String] IS NOT NULL THEN [r].[Id] +END = [o].[RelatedTypeRootEntityId] +LEFT JOIN ( + SELECT [r0].[RootEntityId], [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String], [r1].[RelatedTypeRootEntityId], [r1].[RelatedTypeId], [r1].[Id] AS [Id0], [r1].[Int] AS [Int0], [r1].[Name] AS [Name0], [r1].[String] AS [String0], [r0].[OptionalNested_Id], [r0].[OptionalNested_Int], [r0].[OptionalNested_Name], [r0].[OptionalNested_String], [r0].[RequiredNested_Id], [r0].[RequiredNested_Int], [r0].[RequiredNested_Name], [r0].[RequiredNested_String] + FROM [RelatedCollection] AS [r0] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r1] ON [r0].[RootEntityId] = [r1].[RelatedTypeRootEntityId] AND [r0].[Id] = [r1].[RelatedTypeId] +) AS [s] ON [r].[Id] = [s].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r2] ON [r].[Id] = [r2].[RelatedTypeRootEntityId] +WHERE [r].[RequiredRelated_RequiredNested_Int] = 50 +ORDER BY [r].[Id], [o].[RelatedTypeRootEntityId], [o].[Id], [s].[RootEntityId], [s].[Id], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[Id0], [r2].[RelatedTypeRootEntityId] +"""); + } + + #endregion Simple filters + + [ConditionalFact] + public virtual void Check_all_tests_overridden() + => TestHelpers.AssertAllMethodsOverridden(GetType()); +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingProjectionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingProjectionSqlServerTest.cs index 1598c2b7505..466c6bd6a4b 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingProjectionSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingProjectionSqlServerTest.cs @@ -3,1009 +3,345 @@ namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedTableSplitting; -public class OwnedTableSplittingProjectionSqlServerTest - : OwnedTableSplittingProjectionRelationalTestBase +public class OwnedTableSplittingProjectionSqlServerTest(OwnedTableSplittingSqlServerFixture fixture, ITestOutputHelper testOutputHelper) + : OwnedTableSplittingProjectionRelationalTestBase(fixture, testOutputHelper) { - public OwnedTableSplittingProjectionSqlServerTest(OwnedTableSplittingRelationshipsSqlServerFixture fixture, ITestOutputHelper testOutputHelper) - : base(fixture) - { - Fixture.TestSqlLoggerFactory.Clear(); - Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); - } - public override async Task Select_root(bool async, QueryTrackingBehavior queryTrackingBehavior) { await base.Select_root(async, queryTrackingBehavior); AssertSql( """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r7].[RelationshipsRootId], [r8].[RelationshipsTrunkRelationshipsRootId], [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[RelationshipsTrunkRelationshipsRootId], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r13].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsRootId], [s0].[Id1], [s0].[Name], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsTrunkId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchRelationshipsTrunkId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchRelationshipsTrunkId10], [s0].[RelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsTrunkId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchRelationshipsTrunkId11], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [s0].[RelationshipsBranchRelationshipsTrunkId12], [s0].[RelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsTrunkId11], [s0].[Id10], [s0].[Name0], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s0].[RelationshipsBranchRelationshipsTrunkId13], [s0].[RelationshipsBranchId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s0].[RelationshipsBranchRelationshipsTrunkId100], [s0].[RelationshipsBranchId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s0].[RelationshipsBranchRelationshipsTrunkId110], [s0].[RelationshipsBranchId11], [s0].[Id100], [s0].[Name00], [s0].[Name1], [s0].[Name2], [s0].[Name3], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [s0].[RelationshipsBranchRelationshipsTrunkId14], [s0].[Id11], [s0].[Name4], [s0].[Name5], [s0].[Name6], [s0].[Name7], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [s0].[RelationshipsBranchRelationshipsTrunkId15], [s0].[Id12], [s0].[Name8], [s0].[Name9], [s0].[Name10], [r0].[Name], [s1].[RelationshipsTrunkRelationshipsRootId], [s1].[Id1], [s1].[Name], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsBranchId1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s1].[RelationshipsBranchId10], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s1].[RelationshipsBranchId11], [s1].[Id10], [s1].[Name0], [s1].[Name1], [s1].[Name2], [r1].[Name], [r31].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r31].[Id1], [r31].[Name], [r2].[Name], [r3].[Name], [r4].[Name], [r32].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r32].[Id1], [r32].[Name], [r5].[Name], [r6].[Name], [r7].[Name], [s2].[RelationshipsTrunkRelationshipsRootId], [s2].[Id1], [s2].[Name], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s2].[RelationshipsBranchId1], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s2].[RelationshipsBranchId10], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s2].[RelationshipsBranchId11], [s2].[Id10], [s2].[Name0], [s2].[Name1], [s2].[Name2], [r8].[Name], [r37].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r37].[Id1], [r37].[Name], [r9].[Name], [r10].[Name], [r11].[Name], [r38].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r38].[Id1], [r38].[Name], [r12].[Name], [r13].[Name] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN [Root_OptionalReferenceTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch] AS [r1] ON [r0].[RelationshipsRootId] = [r1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r3] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch] AS [r4] ON [r0].[RelationshipsRootId] = [r4].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r5] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r6] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk] AS [r7] ON [r].[Id] = [r7].[RelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch] AS [r8] ON [r7].[RelationshipsRootId] = [r8].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r9] ON [r8].[RelationshipsTrunkRelationshipsRootId] = [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r10] ON [r8].[RelationshipsTrunkRelationshipsRootId] = [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch] AS [r11] ON [r7].[RelationshipsRootId] = [r11].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r12] ON [r11].[RelationshipsTrunkRelationshipsRootId] = [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r13] ON [r11].[RelationshipsTrunkRelationshipsRootId] = [r13].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r14].[RelationshipsRootId], [r14].[Id1], [r14].[Name], [r15].[RelationshipsTrunkRelationshipsRootId], [r15].[RelationshipsTrunkId1], [r16].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r16].[RelationshipsBranchRelationshipsTrunkId1], [r17].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r17].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r18].[RelationshipsTrunkRelationshipsRootId] AS [RelationshipsTrunkRelationshipsRootId0], [r18].[RelationshipsTrunkId1] AS [RelationshipsTrunkId10], [r19].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r19].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r20].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [r20].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId12], [s].[RelationshipsTrunkRelationshipsRootId] AS [RelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsTrunkId1] AS [RelationshipsTrunkId11], [s].[Id1] AS [Id10], [s].[Name] AS [Name0], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId13], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s].[RelationshipsBranchRelationshipsTrunkId10] AS [RelationshipsBranchRelationshipsTrunkId100], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s].[RelationshipsBranchRelationshipsTrunkId11] AS [RelationshipsBranchRelationshipsTrunkId110], [s].[RelationshipsBranchId11], [s].[Id10] AS [Id100], [s].[Name0] AS [Name00], [s].[Name1], [s].[Name2], [r15].[Name] AS [Name3], [r25].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [r25].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId14], [r25].[Id1] AS [Id11], [r25].[Name] AS [Name4], [r16].[Name] AS [Name5], [r17].[Name] AS [Name6], [r18].[Name] AS [Name7], [r26].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [r26].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId15], [r26].[Id1] AS [Id12], [r26].[Name] AS [Name8], [r19].[Name] AS [Name9], [r20].[Name] AS [Name10] - FROM [RelationshipsRoot_CollectionTrunk] AS [r14] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch] AS [r15] ON [r14].[RelationshipsRootId] = [r15].[RelationshipsTrunkRelationshipsRootId] AND [r14].[Id1] = [r15].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r16] ON [r15].[RelationshipsTrunkRelationshipsRootId] = [r16].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r15].[RelationshipsTrunkId1] = [r16].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r17] ON [r15].[RelationshipsTrunkRelationshipsRootId] = [r17].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r15].[RelationshipsTrunkId1] = [r17].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch] AS [r18] ON [r14].[RelationshipsRootId] = [r18].[RelationshipsTrunkRelationshipsRootId] AND [r14].[Id1] = [r18].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r19] ON [r18].[RelationshipsTrunkRelationshipsRootId] = [r19].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r18].[RelationshipsTrunkId1] = [r19].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r20] ON [r18].[RelationshipsTrunkRelationshipsRootId] = [r20].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r18].[RelationshipsTrunkId1] = [r20].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN ( - SELECT [r21].[RelationshipsTrunkRelationshipsRootId], [r21].[RelationshipsTrunkId1], [r21].[Id1], [r21].[Name], [r22].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r22].[RelationshipsBranchRelationshipsTrunkId1], [r22].[RelationshipsBranchId1], [r23].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r23].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r23].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r24].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r24].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r24].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r24].[Id1] AS [Id10], [r24].[Name] AS [Name0], [r22].[Name] AS [Name1], [r23].[Name] AS [Name2] - FROM [RelationshipsRoot_CollectionTrunk_CollectionBranch] AS [r21] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r22] ON [r21].[RelationshipsTrunkRelationshipsRootId] = [r22].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r21].[RelationshipsTrunkId1] = [r22].[RelationshipsBranchRelationshipsTrunkId1] AND [r21].[Id1] = [r22].[RelationshipsBranchId1] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r23] ON [r21].[RelationshipsTrunkRelationshipsRootId] = [r23].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r21].[RelationshipsTrunkId1] = [r23].[RelationshipsBranchRelationshipsTrunkId1] AND [r21].[Id1] = [r23].[RelationshipsBranchId1] - LEFT JOIN [RelationshipsRoot_CollectionTrunk_CollectionBranch_CollectionLeaf] AS [r24] ON [r21].[RelationshipsTrunkRelationshipsRootId] = [r24].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r21].[RelationshipsTrunkId1] = [r24].[RelationshipsBranchRelationshipsTrunkId1] AND [r21].[Id1] = [r24].[RelationshipsBranchId1] - ) AS [s] ON [r14].[RelationshipsRootId] = [s].[RelationshipsTrunkRelationshipsRootId] AND [r14].[Id1] = [s].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r25] ON [r15].[RelationshipsTrunkRelationshipsRootId] = [r25].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r15].[RelationshipsTrunkId1] = [r25].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r26] ON [r18].[RelationshipsTrunkRelationshipsRootId] = [r26].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r18].[RelationshipsTrunkId1] = [r26].[RelationshipsBranchRelationshipsTrunkId1] -) AS [s0] ON [r].[Id] = [s0].[RelationshipsRootId] -LEFT JOIN ( - SELECT [r27].[RelationshipsTrunkRelationshipsRootId], [r27].[Id1], [r27].[Name], [r28].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r28].[RelationshipsBranchId1], [r29].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r29].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r30].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r30].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r30].[Id1] AS [Id10], [r30].[Name] AS [Name0], [r28].[Name] AS [Name1], [r29].[Name] AS [Name2] - FROM [Root_OptionalReferenceTrunk_CollectionBranch] AS [r27] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r28] ON [r27].[RelationshipsTrunkRelationshipsRootId] = [r28].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r27].[Id1] = [r28].[RelationshipsBranchId1] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r29] ON [r27].[RelationshipsTrunkRelationshipsRootId] = [r29].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r27].[Id1] = [r29].[RelationshipsBranchId1] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r30] ON [r27].[RelationshipsTrunkRelationshipsRootId] = [r30].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r27].[Id1] = [r30].[RelationshipsBranchId1] -) AS [s1] ON [r0].[RelationshipsRootId] = [s1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r31] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r31].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r32] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r32].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r33].[RelationshipsTrunkRelationshipsRootId], [r33].[Id1], [r33].[Name], [r34].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r34].[RelationshipsBranchId1], [r35].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r35].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r36].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r36].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r36].[Id1] AS [Id10], [r36].[Name] AS [Name0], [r34].[Name] AS [Name1], [r35].[Name] AS [Name2] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r33] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r34] ON [r33].[RelationshipsTrunkRelationshipsRootId] = [r34].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r33].[Id1] = [r34].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r35] ON [r33].[RelationshipsTrunkRelationshipsRootId] = [r35].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r33].[Id1] = [r35].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r36] ON [r33].[RelationshipsTrunkRelationshipsRootId] = [r36].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r33].[Id1] = [r36].[RelationshipsBranchId1] -) AS [s2] ON [r7].[RelationshipsRootId] = [s2].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r37] ON [r8].[RelationshipsTrunkRelationshipsRootId] = [r37].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r38] ON [r11].[RelationshipsTrunkRelationshipsRootId] = [r38].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r7].[RelationshipsRootId], [r8].[RelationshipsTrunkRelationshipsRootId], [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[RelationshipsTrunkRelationshipsRootId], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r13].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsRootId], [s0].[Id1], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsTrunkId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchRelationshipsTrunkId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchRelationshipsTrunkId10], [s0].[RelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsTrunkId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchRelationshipsTrunkId11], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [s0].[RelationshipsBranchRelationshipsTrunkId12], [s0].[RelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsTrunkId11], [s0].[Id10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s0].[RelationshipsBranchRelationshipsTrunkId13], [s0].[RelationshipsBranchId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s0].[RelationshipsBranchRelationshipsTrunkId100], [s0].[RelationshipsBranchId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s0].[RelationshipsBranchRelationshipsTrunkId110], [s0].[RelationshipsBranchId11], [s0].[Id100], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [s0].[RelationshipsBranchRelationshipsTrunkId14], [s0].[Id11], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [s0].[RelationshipsBranchRelationshipsTrunkId15], [s0].[Id12], [s1].[RelationshipsTrunkRelationshipsRootId], [s1].[Id1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsBranchId1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s1].[RelationshipsBranchId10], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s1].[RelationshipsBranchId11], [s1].[Id10], [r31].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r31].[Id1], [r32].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r32].[Id1], [s2].[RelationshipsTrunkRelationshipsRootId], [s2].[Id1], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s2].[RelationshipsBranchId1], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s2].[RelationshipsBranchId10], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s2].[RelationshipsBranchId11], [s2].[Id10], [r37].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r37].[Id1], [r38].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated_Id], [r].[OptionalRelated_Int], [r].[OptionalRelated_Name], [r].[OptionalRelated_String], [o].[RelatedTypeRootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String], [r].[OptionalRelated_OptionalNested_Id], [r].[OptionalRelated_OptionalNested_Int], [r].[OptionalRelated_OptionalNested_Name], [r].[OptionalRelated_OptionalNested_String], [r].[OptionalRelated_RequiredNested_Id], [r].[OptionalRelated_RequiredNested_Int], [r].[OptionalRelated_RequiredNested_Name], [r].[OptionalRelated_RequiredNested_String], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[Id0], [s].[Int0], [s].[Name0], [s].[String0], [s].[OptionalNested_Id], [s].[OptionalNested_Int], [s].[OptionalNested_Name], [s].[OptionalNested_String], [s].[RequiredNested_Id], [s].[RequiredNested_Int], [s].[RequiredNested_Name], [s].[RequiredNested_String], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r2].[RelatedTypeRootEntityId], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String], [r].[RequiredRelated_OptionalNested_Id], [r].[RequiredRelated_OptionalNested_Int], [r].[RequiredRelated_OptionalNested_Name], [r].[RequiredRelated_OptionalNested_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o] ON CASE + WHEN [r].[OptionalRelated_Id] IS NOT NULL AND [r].[OptionalRelated_Int] IS NOT NULL AND [r].[OptionalRelated_Name] IS NOT NULL AND [r].[OptionalRelated_String] IS NOT NULL THEN [r].[Id] +END = [o].[RelatedTypeRootEntityId] +LEFT JOIN ( + SELECT [r0].[RootEntityId], [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String], [r1].[RelatedTypeRootEntityId], [r1].[RelatedTypeId], [r1].[Id] AS [Id0], [r1].[Int] AS [Int0], [r1].[Name] AS [Name0], [r1].[String] AS [String0], [r0].[OptionalNested_Id], [r0].[OptionalNested_Int], [r0].[OptionalNested_Name], [r0].[OptionalNested_String], [r0].[RequiredNested_Id], [r0].[RequiredNested_Int], [r0].[RequiredNested_Name], [r0].[RequiredNested_String] + FROM [RelatedCollection] AS [r0] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r1] ON [r0].[RootEntityId] = [r1].[RelatedTypeRootEntityId] AND [r0].[Id] = [r1].[RelatedTypeId] +) AS [s] ON [r].[Id] = [s].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r2] ON [r].[Id] = [r2].[RelatedTypeRootEntityId] +ORDER BY [r].[Id], [o].[RelatedTypeRootEntityId], [o].[Id], [s].[RootEntityId], [s].[Id], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[Id0], [r2].[RelatedTypeRootEntityId] """); } - public override async Task Select_trunk_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) + #region Simple properties + + public override async Task Select_related_property(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_trunk_optional(async, queryTrackingBehavior); + await base.Select_related_property(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r0].[RelationshipsRootId], [r0].[Name], [r].[Id], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchId11], [s].[Id10], [s].[Name0], [s].[Name1], [s].[Name2], [r1].[Name], [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[Id1], [r11].[Name], [r2].[Name], [r3].[Name], [r4].[Name], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r12].[Id1], [r12].[Name], [r5].[Name], [r6].[Name] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN [Root_OptionalReferenceTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch] AS [r1] ON [r0].[RelationshipsRootId] = [r1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r3] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch] AS [r4] ON [r0].[RelationshipsRootId] = [r4].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r5] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r6] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r7].[RelationshipsTrunkRelationshipsRootId], [r7].[Id1], [r7].[Name], [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r8].[RelationshipsBranchId1], [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r9].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r10].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r10].[Id1] AS [Id10], [r10].[Name] AS [Name0], [r8].[Name] AS [Name1], [r9].[Name] AS [Name2] - FROM [Root_OptionalReferenceTrunk_CollectionBranch] AS [r7] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r8] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r7].[Id1] = [r8].[RelationshipsBranchId1] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r9] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r7].[Id1] = [r9].[RelationshipsBranchId1] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r10] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r7].[Id1] = [r10].[RelationshipsBranchId1] -) AS [s] ON [r0].[RelationshipsRootId] = [s].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r11] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r12] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchId11], [s].[Id10], [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[Id1], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] + AssertSql( + """ +SELECT [r].[RequiredRelated_String] +FROM [RootEntity] AS [r] """); - } } - public override async Task Select_trunk_required(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_property(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_trunk_required(async, queryTrackingBehavior); + await base.Select_optional_related_property(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r0].[RelationshipsRootId], [r0].[Name], [r].[Id], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchId11], [s].[Id10], [s].[Name0], [s].[Name1], [s].[Name2], [r1].[Name], [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[Id1], [r11].[Name], [r2].[Name], [r3].[Name], [r4].[Name], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r12].[Id1], [r12].[Name], [r5].[Name], [r6].[Name] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN [Root_RequiredReferenceTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch] AS [r1] ON [r0].[RelationshipsRootId] = [r1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r3] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch] AS [r4] ON [r0].[RelationshipsRootId] = [r4].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r5] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r6] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r7].[RelationshipsTrunkRelationshipsRootId], [r7].[Id1], [r7].[Name], [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r8].[RelationshipsBranchId1], [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r9].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r10].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r10].[Id1] AS [Id10], [r10].[Name] AS [Name0], [r8].[Name] AS [Name1], [r9].[Name] AS [Name2] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r7] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r8] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r7].[Id1] = [r8].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r9] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r7].[Id1] = [r9].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r10] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r7].[Id1] = [r10].[RelationshipsBranchId1] -) AS [s] ON [r0].[RelationshipsRootId] = [s].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r11] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r12] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchId11], [s].[Id10], [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[Id1], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] + AssertSql( + """ +SELECT [r].[OptionalRelated_String] +FROM [RootEntity] AS [r] """); - } } - public override async Task Select_trunk_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_property_value_type(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_trunk_collection(async, queryTrackingBehavior); + await base.Select_optional_related_property_value_type(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r].[Id], [s0].[RelationshipsRootId], [s0].[Id1], [s0].[Name], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsTrunkId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchRelationshipsTrunkId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchRelationshipsTrunkId10], [s0].[RelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsTrunkId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchRelationshipsTrunkId11], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [s0].[RelationshipsBranchRelationshipsTrunkId12], [s0].[RelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsTrunkId11], [s0].[Id10], [s0].[Name0], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s0].[RelationshipsBranchRelationshipsTrunkId13], [s0].[RelationshipsBranchId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s0].[RelationshipsBranchRelationshipsTrunkId100], [s0].[RelationshipsBranchId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s0].[RelationshipsBranchRelationshipsTrunkId110], [s0].[RelationshipsBranchId11], [s0].[Id100], [s0].[Name00], [s0].[Name1], [s0].[Name2], [s0].[Name3], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [s0].[RelationshipsBranchRelationshipsTrunkId14], [s0].[Id11], [s0].[Name4], [s0].[Name5], [s0].[Name6], [s0].[Name7], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [s0].[RelationshipsBranchRelationshipsTrunkId15], [s0].[Id12], [s0].[Name8], [s0].[Name9], [s0].[Name10] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN ( - SELECT [r0].[RelationshipsRootId], [r0].[Id1], [r0].[Name], [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[RelationshipsTrunkId1], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkId1], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r3].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r4].[RelationshipsTrunkRelationshipsRootId] AS [RelationshipsTrunkRelationshipsRootId0], [r4].[RelationshipsTrunkId1] AS [RelationshipsTrunkId10], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r5].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [r6].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId12], [s].[RelationshipsTrunkRelationshipsRootId] AS [RelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsTrunkId1] AS [RelationshipsTrunkId11], [s].[Id1] AS [Id10], [s].[Name] AS [Name0], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId13], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s].[RelationshipsBranchRelationshipsTrunkId10] AS [RelationshipsBranchRelationshipsTrunkId100], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s].[RelationshipsBranchRelationshipsTrunkId11] AS [RelationshipsBranchRelationshipsTrunkId110], [s].[RelationshipsBranchId11], [s].[Id10] AS [Id100], [s].[Name0] AS [Name00], [s].[Name1], [s].[Name2], [r1].[Name] AS [Name3], [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [r11].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId14], [r11].[Id1] AS [Id11], [r11].[Name] AS [Name4], [r2].[Name] AS [Name5], [r3].[Name] AS [Name6], [r4].[Name] AS [Name7], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [r12].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId15], [r12].[Id1] AS [Id12], [r12].[Name] AS [Name8], [r5].[Name] AS [Name9], [r6].[Name] AS [Name10] - FROM [RelationshipsRoot_CollectionTrunk] AS [r0] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch] AS [r1] ON [r0].[RelationshipsRootId] = [r1].[RelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [r1].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[RelationshipsTrunkId1] = [r2].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r3] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[RelationshipsTrunkId1] = [r3].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch] AS [r4] ON [r0].[RelationshipsRootId] = [r4].[RelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [r4].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r5] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r4].[RelationshipsTrunkId1] = [r5].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r6] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r4].[RelationshipsTrunkId1] = [r6].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN ( - SELECT [r7].[RelationshipsTrunkRelationshipsRootId], [r7].[RelationshipsTrunkId1], [r7].[Id1], [r7].[Name], [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r8].[RelationshipsBranchRelationshipsTrunkId1], [r8].[RelationshipsBranchId1], [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r9].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r9].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r10].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r10].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r10].[Id1] AS [Id10], [r10].[Name] AS [Name0], [r8].[Name] AS [Name1], [r9].[Name] AS [Name2] - FROM [RelationshipsRoot_CollectionTrunk_CollectionBranch] AS [r7] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r8] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r7].[RelationshipsTrunkId1] = [r8].[RelationshipsBranchRelationshipsTrunkId1] AND [r7].[Id1] = [r8].[RelationshipsBranchId1] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r9] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r7].[RelationshipsTrunkId1] = [r9].[RelationshipsBranchRelationshipsTrunkId1] AND [r7].[Id1] = [r9].[RelationshipsBranchId1] - LEFT JOIN [RelationshipsRoot_CollectionTrunk_CollectionBranch_CollectionLeaf] AS [r10] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r7].[RelationshipsTrunkId1] = [r10].[RelationshipsBranchRelationshipsTrunkId1] AND [r7].[Id1] = [r10].[RelationshipsBranchId1] - ) AS [s] ON [r0].[RelationshipsRootId] = [s].[RelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [s].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r11] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[RelationshipsTrunkId1] = [r11].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r12] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r4].[RelationshipsTrunkId1] = [r12].[RelationshipsBranchRelationshipsTrunkId1] -) AS [s0] ON [r].[Id] = [s0].[RelationshipsRootId] -ORDER BY [r].[Id], [s0].[RelationshipsRootId], [s0].[Id1], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsTrunkId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchRelationshipsTrunkId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchRelationshipsTrunkId10], [s0].[RelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsTrunkId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchRelationshipsTrunkId11], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [s0].[RelationshipsBranchRelationshipsTrunkId12], [s0].[RelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsTrunkId11], [s0].[Id10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s0].[RelationshipsBranchRelationshipsTrunkId13], [s0].[RelationshipsBranchId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s0].[RelationshipsBranchRelationshipsTrunkId100], [s0].[RelationshipsBranchId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s0].[RelationshipsBranchRelationshipsTrunkId110], [s0].[RelationshipsBranchId11], [s0].[Id100], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [s0].[RelationshipsBranchRelationshipsTrunkId14], [s0].[Id11], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [s0].[RelationshipsBranchRelationshipsTrunkId15] + AssertSql( + """ +SELECT [r].[OptionalRelated_Int] +FROM [RootEntity] AS [r] """); - } } - public override async Task Select_branch_required_required(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_branch_required_required(async, queryTrackingBehavior); + #endregion Simple properties - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[Name], [r].[Id], [r0].[RelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[Id1], [r4].[Name], [r2].[Name], [r3].[Name] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN [Root_RequiredReferenceTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch] AS [r1] ON [r0].[RelationshipsRootId] = [r1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r3] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r4] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -"""); - } - } + #region Non-collection - public override async Task Select_branch_required_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_related(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_branch_required_optional(async, queryTrackingBehavior); + await base.Select_related(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[Name], [r].[Id], [r0].[RelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[Id1], [r4].[Name], [r2].[Name], [r3].[Name] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN [Root_RequiredReferenceTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch] AS [r1] ON [r0].[RelationshipsRootId] = [r1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r3] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r4] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] +SELECT [r].[Id], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r0].[RelatedTypeRootEntityId], [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String], [r].[RequiredRelated_OptionalNested_Id], [r].[RequiredRelated_OptionalNested_Int], [r].[RequiredRelated_OptionalNested_Name], [r].[RequiredRelated_OptionalNested_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r0] ON [r].[Id] = [r0].[RelatedTypeRootEntityId] +ORDER BY [r].[Id], [r0].[RelatedTypeRootEntityId] """); } } - public override async Task Select_branch_optional_required(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_branch_optional_required(async, queryTrackingBehavior); + await base.Select_optional_related(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[Name], [r].[Id], [r0].[RelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[Id1], [r4].[Name], [r2].[Name], [r3].[Name] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN [Root_RequiredReferenceTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch] AS [r1] ON [r0].[RelationshipsRootId] = [r1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r3] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r4] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] +SELECT [r].[Id], [r].[OptionalRelated_Id], [r].[OptionalRelated_Int], [r].[OptionalRelated_Name], [r].[OptionalRelated_String], [o].[RelatedTypeRootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String], [r].[OptionalRelated_OptionalNested_Id], [r].[OptionalRelated_OptionalNested_Int], [r].[OptionalRelated_OptionalNested_Name], [r].[OptionalRelated_OptionalNested_String], [r].[OptionalRelated_RequiredNested_Id], [r].[OptionalRelated_RequiredNested_Int], [r].[OptionalRelated_RequiredNested_Name], [r].[OptionalRelated_RequiredNested_String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o] ON CASE + WHEN [r].[OptionalRelated_Id] IS NOT NULL AND [r].[OptionalRelated_Int] IS NOT NULL AND [r].[OptionalRelated_Name] IS NOT NULL AND [r].[OptionalRelated_String] IS NOT NULL THEN [r].[Id] +END = [o].[RelatedTypeRootEntityId] +ORDER BY [r].[Id], [o].[RelatedTypeRootEntityId] """); } } - public override async Task Select_branch_optional_optional(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_required_related_required_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_branch_optional_optional(async, queryTrackingBehavior); + await base.Select_required_related_required_nested(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[Name], [r].[Id], [r0].[RelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[Id1], [r4].[Name], [r2].[Name], [r3].[Name] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN [Root_RequiredReferenceTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch] AS [r1] ON [r0].[RelationshipsRootId] = [r1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r3] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r4] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] +SELECT [r].[Id], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] """); } } - public override async Task Select_branch_required_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_required_related_optional_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_branch_required_collection(async, queryTrackingBehavior); + await base.Select_required_related_optional_nested(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r].[Id], [r0].[RelationshipsRootId], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchId11], [s].[Id10], [s].[Name0], [s].[Name1], [s].[Name2] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN [Root_RequiredReferenceTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -LEFT JOIN ( - SELECT [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[Id1], [r1].[Name], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchId1], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r3].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r4].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r4].[Id1] AS [Id10], [r4].[Name] AS [Name0], [r2].[Name] AS [Name1], [r3].[Name] AS [Name2] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[Id1] = [r2].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r3] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[Id1] = [r3].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r4] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[Id1] = [r4].[RelationshipsBranchId1] -) AS [s] ON [r0].[RelationshipsRootId] = [s].[RelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchId11] +SELECT [r].[Id], [r].[RequiredRelated_OptionalNested_Id], [r].[RequiredRelated_OptionalNested_Int], [r].[RequiredRelated_OptionalNested_Name], [r].[RequiredRelated_OptionalNested_String] +FROM [RootEntity] AS [r] """); } } - public override async Task Select_branch_optional_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_required_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_branch_optional_collection(async, queryTrackingBehavior); + await base.Select_optional_related_required_nested(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r].[Id], [r0].[RelationshipsRootId], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchId11], [s].[Id10], [s].[Name0], [s].[Name1], [s].[Name2] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN [Root_RequiredReferenceTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -LEFT JOIN ( - SELECT [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[Id1], [r1].[Name], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchId1], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r3].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r4].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r4].[Id1] AS [Id10], [r4].[Name] AS [Name0], [r2].[Name] AS [Name1], [r3].[Name] AS [Name2] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[Id1] = [r2].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r3] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[Id1] = [r3].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r4] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[Id1] = [r4].[RelationshipsBranchId1] -) AS [s] ON [r0].[RelationshipsRootId] = [s].[RelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchId11] +SELECT [r].[Id], [r].[OptionalRelated_RequiredNested_Id], [r].[OptionalRelated_RequiredNested_Int], [r].[OptionalRelated_RequiredNested_Name], [r].[OptionalRelated_RequiredNested_String] +FROM [RootEntity] AS [r] """); } } - #region Multiple - - public override async Task Select_root_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_optional_nested(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_root_duplicated(async, queryTrackingBehavior); + await base.Select_optional_related_optional_nested(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql( - """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r7].[RelationshipsRootId], [r8].[RelationshipsTrunkRelationshipsRootId], [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[RelationshipsTrunkRelationshipsRootId], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r13].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsRootId], [s0].[Id1], [s0].[Name], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsTrunkId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchRelationshipsTrunkId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchRelationshipsTrunkId10], [s0].[RelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsTrunkId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchRelationshipsTrunkId11], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [s0].[RelationshipsBranchRelationshipsTrunkId12], [s0].[RelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsTrunkId11], [s0].[Id10], [s0].[Name0], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s0].[RelationshipsBranchRelationshipsTrunkId13], [s0].[RelationshipsBranchId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s0].[RelationshipsBranchRelationshipsTrunkId100], [s0].[RelationshipsBranchId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s0].[RelationshipsBranchRelationshipsTrunkId110], [s0].[RelationshipsBranchId11], [s0].[Id100], [s0].[Name00], [s0].[Name1], [s0].[Name2], [s0].[Name3], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [s0].[RelationshipsBranchRelationshipsTrunkId14], [s0].[Id11], [s0].[Name4], [s0].[Name5], [s0].[Name6], [s0].[Name7], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [s0].[RelationshipsBranchRelationshipsTrunkId15], [s0].[Id12], [s0].[Name8], [s0].[Name9], [s0].[Name10], [r0].[Name], [s1].[RelationshipsTrunkRelationshipsRootId], [s1].[Id1], [s1].[Name], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsBranchId1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s1].[RelationshipsBranchId10], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s1].[RelationshipsBranchId11], [s1].[Id10], [s1].[Name0], [s1].[Name1], [s1].[Name2], [r1].[Name], [r31].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r31].[Id1], [r31].[Name], [r2].[Name], [r3].[Name], [r4].[Name], [r32].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r32].[Id1], [r32].[Name], [r5].[Name], [r6].[Name], [r7].[Name], [s2].[RelationshipsTrunkRelationshipsRootId], [s2].[Id1], [s2].[Name], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s2].[RelationshipsBranchId1], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s2].[RelationshipsBranchId10], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s2].[RelationshipsBranchId11], [s2].[Id10], [s2].[Name0], [s2].[Name1], [s2].[Name2], [r8].[Name], [r37].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r37].[Id1], [r37].[Name], [r9].[Name], [r10].[Name], [r11].[Name], [r38].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r38].[Id1], [r38].[Name], [r12].[Name], [r13].[Name], [s4].[RelationshipsRootId], [s4].[Id1], [s4].[Name], [s4].[RelationshipsTrunkRelationshipsRootId], [s4].[RelationshipsTrunkId1], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s4].[RelationshipsBranchRelationshipsTrunkId1], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s4].[RelationshipsBranchRelationshipsTrunkId10], [s4].[RelationshipsTrunkRelationshipsRootId0], [s4].[RelationshipsTrunkId10], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s4].[RelationshipsBranchRelationshipsTrunkId11], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [s4].[RelationshipsBranchRelationshipsTrunkId12], [s4].[RelationshipsTrunkRelationshipsRootId1], [s4].[RelationshipsTrunkId11], [s4].[Id10], [s4].[Name0], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s4].[RelationshipsBranchRelationshipsTrunkId13], [s4].[RelationshipsBranchId1], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s4].[RelationshipsBranchRelationshipsTrunkId100], [s4].[RelationshipsBranchId10], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s4].[RelationshipsBranchRelationshipsTrunkId110], [s4].[RelationshipsBranchId11], [s4].[Id100], [s4].[Name00], [s4].[Name1], [s4].[Name2], [s4].[Name3], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [s4].[RelationshipsBranchRelationshipsTrunkId14], [s4].[Id11], [s4].[Name4], [s4].[Name5], [s4].[Name6], [s4].[Name7], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [s4].[RelationshipsBranchRelationshipsTrunkId15], [s4].[Id12], [s4].[Name8], [s4].[Name9], [s4].[Name10], [s5].[RelationshipsTrunkRelationshipsRootId], [s5].[Id1], [s5].[Name], [s5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s5].[RelationshipsBranchId1], [s5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s5].[RelationshipsBranchId10], [s5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s5].[RelationshipsBranchId11], [s5].[Id10], [s5].[Name0], [s5].[Name1], [s5].[Name2], [r56].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r56].[Id1], [r56].[Name], [r57].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r57].[Id1], [r57].[Name], [s6].[RelationshipsTrunkRelationshipsRootId], [s6].[Id1], [s6].[Name], [s6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s6].[RelationshipsBranchId1], [s6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s6].[RelationshipsBranchId10], [s6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s6].[RelationshipsBranchId11], [s6].[Id10], [s6].[Name0], [s6].[Name1], [s6].[Name2], [r62].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r62].[Id1], [r62].[Name], [r63].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r63].[Id1], [r63].[Name] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN [Root_OptionalReferenceTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch] AS [r1] ON [r0].[RelationshipsRootId] = [r1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r3] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch] AS [r4] ON [r0].[RelationshipsRootId] = [r4].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r5] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r6] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk] AS [r7] ON [r].[Id] = [r7].[RelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch] AS [r8] ON [r7].[RelationshipsRootId] = [r8].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r9] ON [r8].[RelationshipsTrunkRelationshipsRootId] = [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r10] ON [r8].[RelationshipsTrunkRelationshipsRootId] = [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch] AS [r11] ON [r7].[RelationshipsRootId] = [r11].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r12] ON [r11].[RelationshipsTrunkRelationshipsRootId] = [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r13] ON [r11].[RelationshipsTrunkRelationshipsRootId] = [r13].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r14].[RelationshipsRootId], [r14].[Id1], [r14].[Name], [r15].[RelationshipsTrunkRelationshipsRootId], [r15].[RelationshipsTrunkId1], [r16].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r16].[RelationshipsBranchRelationshipsTrunkId1], [r17].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r17].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r18].[RelationshipsTrunkRelationshipsRootId] AS [RelationshipsTrunkRelationshipsRootId0], [r18].[RelationshipsTrunkId1] AS [RelationshipsTrunkId10], [r19].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r19].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r20].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [r20].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId12], [s].[RelationshipsTrunkRelationshipsRootId] AS [RelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsTrunkId1] AS [RelationshipsTrunkId11], [s].[Id1] AS [Id10], [s].[Name] AS [Name0], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId13], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s].[RelationshipsBranchRelationshipsTrunkId10] AS [RelationshipsBranchRelationshipsTrunkId100], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s].[RelationshipsBranchRelationshipsTrunkId11] AS [RelationshipsBranchRelationshipsTrunkId110], [s].[RelationshipsBranchId11], [s].[Id10] AS [Id100], [s].[Name0] AS [Name00], [s].[Name1], [s].[Name2], [r15].[Name] AS [Name3], [r25].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [r25].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId14], [r25].[Id1] AS [Id11], [r25].[Name] AS [Name4], [r16].[Name] AS [Name5], [r17].[Name] AS [Name6], [r18].[Name] AS [Name7], [r26].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [r26].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId15], [r26].[Id1] AS [Id12], [r26].[Name] AS [Name8], [r19].[Name] AS [Name9], [r20].[Name] AS [Name10] - FROM [RelationshipsRoot_CollectionTrunk] AS [r14] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch] AS [r15] ON [r14].[RelationshipsRootId] = [r15].[RelationshipsTrunkRelationshipsRootId] AND [r14].[Id1] = [r15].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r16] ON [r15].[RelationshipsTrunkRelationshipsRootId] = [r16].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r15].[RelationshipsTrunkId1] = [r16].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r17] ON [r15].[RelationshipsTrunkRelationshipsRootId] = [r17].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r15].[RelationshipsTrunkId1] = [r17].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch] AS [r18] ON [r14].[RelationshipsRootId] = [r18].[RelationshipsTrunkRelationshipsRootId] AND [r14].[Id1] = [r18].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r19] ON [r18].[RelationshipsTrunkRelationshipsRootId] = [r19].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r18].[RelationshipsTrunkId1] = [r19].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r20] ON [r18].[RelationshipsTrunkRelationshipsRootId] = [r20].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r18].[RelationshipsTrunkId1] = [r20].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN ( - SELECT [r21].[RelationshipsTrunkRelationshipsRootId], [r21].[RelationshipsTrunkId1], [r21].[Id1], [r21].[Name], [r22].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r22].[RelationshipsBranchRelationshipsTrunkId1], [r22].[RelationshipsBranchId1], [r23].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r23].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r23].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r24].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r24].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r24].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r24].[Id1] AS [Id10], [r24].[Name] AS [Name0], [r22].[Name] AS [Name1], [r23].[Name] AS [Name2] - FROM [RelationshipsRoot_CollectionTrunk_CollectionBranch] AS [r21] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r22] ON [r21].[RelationshipsTrunkRelationshipsRootId] = [r22].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r21].[RelationshipsTrunkId1] = [r22].[RelationshipsBranchRelationshipsTrunkId1] AND [r21].[Id1] = [r22].[RelationshipsBranchId1] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r23] ON [r21].[RelationshipsTrunkRelationshipsRootId] = [r23].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r21].[RelationshipsTrunkId1] = [r23].[RelationshipsBranchRelationshipsTrunkId1] AND [r21].[Id1] = [r23].[RelationshipsBranchId1] - LEFT JOIN [RelationshipsRoot_CollectionTrunk_CollectionBranch_CollectionLeaf] AS [r24] ON [r21].[RelationshipsTrunkRelationshipsRootId] = [r24].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r21].[RelationshipsTrunkId1] = [r24].[RelationshipsBranchRelationshipsTrunkId1] AND [r21].[Id1] = [r24].[RelationshipsBranchId1] - ) AS [s] ON [r14].[RelationshipsRootId] = [s].[RelationshipsTrunkRelationshipsRootId] AND [r14].[Id1] = [s].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r25] ON [r15].[RelationshipsTrunkRelationshipsRootId] = [r25].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r15].[RelationshipsTrunkId1] = [r25].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r26] ON [r18].[RelationshipsTrunkRelationshipsRootId] = [r26].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r18].[RelationshipsTrunkId1] = [r26].[RelationshipsBranchRelationshipsTrunkId1] -) AS [s0] ON [r].[Id] = [s0].[RelationshipsRootId] -LEFT JOIN ( - SELECT [r27].[RelationshipsTrunkRelationshipsRootId], [r27].[Id1], [r27].[Name], [r28].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r28].[RelationshipsBranchId1], [r29].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r29].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r30].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r30].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r30].[Id1] AS [Id10], [r30].[Name] AS [Name0], [r28].[Name] AS [Name1], [r29].[Name] AS [Name2] - FROM [Root_OptionalReferenceTrunk_CollectionBranch] AS [r27] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r28] ON [r27].[RelationshipsTrunkRelationshipsRootId] = [r28].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r27].[Id1] = [r28].[RelationshipsBranchId1] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r29] ON [r27].[RelationshipsTrunkRelationshipsRootId] = [r29].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r27].[Id1] = [r29].[RelationshipsBranchId1] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r30] ON [r27].[RelationshipsTrunkRelationshipsRootId] = [r30].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r27].[Id1] = [r30].[RelationshipsBranchId1] -) AS [s1] ON [r0].[RelationshipsRootId] = [s1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r31] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r31].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r32] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r32].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r33].[RelationshipsTrunkRelationshipsRootId], [r33].[Id1], [r33].[Name], [r34].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r34].[RelationshipsBranchId1], [r35].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r35].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r36].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r36].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r36].[Id1] AS [Id10], [r36].[Name] AS [Name0], [r34].[Name] AS [Name1], [r35].[Name] AS [Name2] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r33] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r34] ON [r33].[RelationshipsTrunkRelationshipsRootId] = [r34].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r33].[Id1] = [r34].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r35] ON [r33].[RelationshipsTrunkRelationshipsRootId] = [r35].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r33].[Id1] = [r35].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r36] ON [r33].[RelationshipsTrunkRelationshipsRootId] = [r36].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r33].[Id1] = [r36].[RelationshipsBranchId1] -) AS [s2] ON [r7].[RelationshipsRootId] = [s2].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r37] ON [r8].[RelationshipsTrunkRelationshipsRootId] = [r37].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r38] ON [r11].[RelationshipsTrunkRelationshipsRootId] = [r38].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r39].[RelationshipsRootId], [r39].[Id1], [r39].[Name], [r40].[RelationshipsTrunkRelationshipsRootId], [r40].[RelationshipsTrunkId1], [r41].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r41].[RelationshipsBranchRelationshipsTrunkId1], [r42].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r42].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r43].[RelationshipsTrunkRelationshipsRootId] AS [RelationshipsTrunkRelationshipsRootId0], [r43].[RelationshipsTrunkId1] AS [RelationshipsTrunkId10], [r44].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r44].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r45].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [r45].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId12], [s3].[RelationshipsTrunkRelationshipsRootId] AS [RelationshipsTrunkRelationshipsRootId1], [s3].[RelationshipsTrunkId1] AS [RelationshipsTrunkId11], [s3].[Id1] AS [Id10], [s3].[Name] AS [Name0], [s3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s3].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId13], [s3].[RelationshipsBranchId1], [s3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s3].[RelationshipsBranchRelationshipsTrunkId10] AS [RelationshipsBranchRelationshipsTrunkId100], [s3].[RelationshipsBranchId10], [s3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s3].[RelationshipsBranchRelationshipsTrunkId11] AS [RelationshipsBranchRelationshipsTrunkId110], [s3].[RelationshipsBranchId11], [s3].[Id10] AS [Id100], [s3].[Name0] AS [Name00], [s3].[Name1], [s3].[Name2], [r40].[Name] AS [Name3], [r50].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [r50].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId14], [r50].[Id1] AS [Id11], [r50].[Name] AS [Name4], [r41].[Name] AS [Name5], [r42].[Name] AS [Name6], [r43].[Name] AS [Name7], [r51].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [r51].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId15], [r51].[Id1] AS [Id12], [r51].[Name] AS [Name8], [r44].[Name] AS [Name9], [r45].[Name] AS [Name10] - FROM [RelationshipsRoot_CollectionTrunk] AS [r39] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch] AS [r40] ON [r39].[RelationshipsRootId] = [r40].[RelationshipsTrunkRelationshipsRootId] AND [r39].[Id1] = [r40].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r41] ON [r40].[RelationshipsTrunkRelationshipsRootId] = [r41].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r40].[RelationshipsTrunkId1] = [r41].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r42] ON [r40].[RelationshipsTrunkRelationshipsRootId] = [r42].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r40].[RelationshipsTrunkId1] = [r42].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch] AS [r43] ON [r39].[RelationshipsRootId] = [r43].[RelationshipsTrunkRelationshipsRootId] AND [r39].[Id1] = [r43].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r44] ON [r43].[RelationshipsTrunkRelationshipsRootId] = [r44].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r43].[RelationshipsTrunkId1] = [r44].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r45] ON [r43].[RelationshipsTrunkRelationshipsRootId] = [r45].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r43].[RelationshipsTrunkId1] = [r45].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN ( - SELECT [r46].[RelationshipsTrunkRelationshipsRootId], [r46].[RelationshipsTrunkId1], [r46].[Id1], [r46].[Name], [r47].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r47].[RelationshipsBranchRelationshipsTrunkId1], [r47].[RelationshipsBranchId1], [r48].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r48].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r48].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r49].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r49].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r49].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r49].[Id1] AS [Id10], [r49].[Name] AS [Name0], [r47].[Name] AS [Name1], [r48].[Name] AS [Name2] - FROM [RelationshipsRoot_CollectionTrunk_CollectionBranch] AS [r46] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r47] ON [r46].[RelationshipsTrunkRelationshipsRootId] = [r47].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r46].[RelationshipsTrunkId1] = [r47].[RelationshipsBranchRelationshipsTrunkId1] AND [r46].[Id1] = [r47].[RelationshipsBranchId1] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r48] ON [r46].[RelationshipsTrunkRelationshipsRootId] = [r48].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r46].[RelationshipsTrunkId1] = [r48].[RelationshipsBranchRelationshipsTrunkId1] AND [r46].[Id1] = [r48].[RelationshipsBranchId1] - LEFT JOIN [RelationshipsRoot_CollectionTrunk_CollectionBranch_CollectionLeaf] AS [r49] ON [r46].[RelationshipsTrunkRelationshipsRootId] = [r49].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r46].[RelationshipsTrunkId1] = [r49].[RelationshipsBranchRelationshipsTrunkId1] AND [r46].[Id1] = [r49].[RelationshipsBranchId1] - ) AS [s3] ON [r39].[RelationshipsRootId] = [s3].[RelationshipsTrunkRelationshipsRootId] AND [r39].[Id1] = [s3].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r50] ON [r40].[RelationshipsTrunkRelationshipsRootId] = [r50].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r40].[RelationshipsTrunkId1] = [r50].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r51] ON [r43].[RelationshipsTrunkRelationshipsRootId] = [r51].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r43].[RelationshipsTrunkId1] = [r51].[RelationshipsBranchRelationshipsTrunkId1] -) AS [s4] ON [r].[Id] = [s4].[RelationshipsRootId] -LEFT JOIN ( - SELECT [r52].[RelationshipsTrunkRelationshipsRootId], [r52].[Id1], [r52].[Name], [r53].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r53].[RelationshipsBranchId1], [r54].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r54].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r55].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r55].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r55].[Id1] AS [Id10], [r55].[Name] AS [Name0], [r53].[Name] AS [Name1], [r54].[Name] AS [Name2] - FROM [Root_OptionalReferenceTrunk_CollectionBranch] AS [r52] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r53] ON [r52].[RelationshipsTrunkRelationshipsRootId] = [r53].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r52].[Id1] = [r53].[RelationshipsBranchId1] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r54] ON [r52].[RelationshipsTrunkRelationshipsRootId] = [r54].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r52].[Id1] = [r54].[RelationshipsBranchId1] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r55] ON [r52].[RelationshipsTrunkRelationshipsRootId] = [r55].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r52].[Id1] = [r55].[RelationshipsBranchId1] -) AS [s5] ON [r0].[RelationshipsRootId] = [s5].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r56] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r56].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r57] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r57].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r58].[RelationshipsTrunkRelationshipsRootId], [r58].[Id1], [r58].[Name], [r59].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r59].[RelationshipsBranchId1], [r60].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r60].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r61].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r61].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r61].[Id1] AS [Id10], [r61].[Name] AS [Name0], [r59].[Name] AS [Name1], [r60].[Name] AS [Name2] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r58] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r59] ON [r58].[RelationshipsTrunkRelationshipsRootId] = [r59].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r58].[Id1] = [r59].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r60] ON [r58].[RelationshipsTrunkRelationshipsRootId] = [r60].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r58].[Id1] = [r60].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r61] ON [r58].[RelationshipsTrunkRelationshipsRootId] = [r61].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r58].[Id1] = [r61].[RelationshipsBranchId1] -) AS [s6] ON [r7].[RelationshipsRootId] = [s6].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r62] ON [r8].[RelationshipsTrunkRelationshipsRootId] = [r62].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r63] ON [r11].[RelationshipsTrunkRelationshipsRootId] = [r63].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r7].[RelationshipsRootId], [r8].[RelationshipsTrunkRelationshipsRootId], [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[RelationshipsTrunkRelationshipsRootId], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r13].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsRootId], [s0].[Id1], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsTrunkId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchRelationshipsTrunkId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchRelationshipsTrunkId10], [s0].[RelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsTrunkId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchRelationshipsTrunkId11], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [s0].[RelationshipsBranchRelationshipsTrunkId12], [s0].[RelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsTrunkId11], [s0].[Id10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s0].[RelationshipsBranchRelationshipsTrunkId13], [s0].[RelationshipsBranchId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s0].[RelationshipsBranchRelationshipsTrunkId100], [s0].[RelationshipsBranchId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s0].[RelationshipsBranchRelationshipsTrunkId110], [s0].[RelationshipsBranchId11], [s0].[Id100], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [s0].[RelationshipsBranchRelationshipsTrunkId14], [s0].[Id11], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [s0].[RelationshipsBranchRelationshipsTrunkId15], [s0].[Id12], [s1].[RelationshipsTrunkRelationshipsRootId], [s1].[Id1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsBranchId1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s1].[RelationshipsBranchId10], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s1].[RelationshipsBranchId11], [s1].[Id10], [r31].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r31].[Id1], [r32].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r32].[Id1], [s2].[RelationshipsTrunkRelationshipsRootId], [s2].[Id1], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s2].[RelationshipsBranchId1], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s2].[RelationshipsBranchId10], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s2].[RelationshipsBranchId11], [s2].[Id10], [r37].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r37].[Id1], [r38].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r38].[Id1], [s4].[RelationshipsRootId], [s4].[Id1], [s4].[RelationshipsTrunkRelationshipsRootId], [s4].[RelationshipsTrunkId1], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s4].[RelationshipsBranchRelationshipsTrunkId1], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s4].[RelationshipsBranchRelationshipsTrunkId10], [s4].[RelationshipsTrunkRelationshipsRootId0], [s4].[RelationshipsTrunkId10], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s4].[RelationshipsBranchRelationshipsTrunkId11], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [s4].[RelationshipsBranchRelationshipsTrunkId12], [s4].[RelationshipsTrunkRelationshipsRootId1], [s4].[RelationshipsTrunkId11], [s4].[Id10], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s4].[RelationshipsBranchRelationshipsTrunkId13], [s4].[RelationshipsBranchId1], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s4].[RelationshipsBranchRelationshipsTrunkId100], [s4].[RelationshipsBranchId10], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s4].[RelationshipsBranchRelationshipsTrunkId110], [s4].[RelationshipsBranchId11], [s4].[Id100], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [s4].[RelationshipsBranchRelationshipsTrunkId14], [s4].[Id11], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [s4].[RelationshipsBranchRelationshipsTrunkId15], [s4].[Id12], [s5].[RelationshipsTrunkRelationshipsRootId], [s5].[Id1], [s5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s5].[RelationshipsBranchId1], [s5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s5].[RelationshipsBranchId10], [s5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s5].[RelationshipsBranchId11], [s5].[Id10], [r56].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r56].[Id1], [r57].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r57].[Id1], [s6].[RelationshipsTrunkRelationshipsRootId], [s6].[Id1], [s6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s6].[RelationshipsBranchId1], [s6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s6].[RelationshipsBranchId10], [s6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s6].[RelationshipsBranchId11], [s6].[Id10], [r62].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r62].[Id1], [r63].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -"""); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r].[Id], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r7].[RelationshipsRootId], [r8].[RelationshipsTrunkRelationshipsRootId], [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[RelationshipsTrunkRelationshipsRootId], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r13].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsRootId], [s0].[Id1], [s0].[Name], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsTrunkId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchRelationshipsTrunkId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchRelationshipsTrunkId10], [s0].[RelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsTrunkId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchRelationshipsTrunkId11], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [s0].[RelationshipsBranchRelationshipsTrunkId12], [s0].[RelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsTrunkId11], [s0].[Id10], [s0].[Name0], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s0].[RelationshipsBranchRelationshipsTrunkId13], [s0].[RelationshipsBranchId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s0].[RelationshipsBranchRelationshipsTrunkId100], [s0].[RelationshipsBranchId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s0].[RelationshipsBranchRelationshipsTrunkId110], [s0].[RelationshipsBranchId11], [s0].[Id100], [s0].[Name00], [s0].[Name1], [s0].[Name2], [s0].[Name3], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [s0].[RelationshipsBranchRelationshipsTrunkId14], [s0].[Id11], [s0].[Name4], [s0].[Name5], [s0].[Name6], [s0].[Name7], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [s0].[RelationshipsBranchRelationshipsTrunkId15], [s0].[Id12], [s0].[Name8], [s0].[Name9], [s0].[Name10], [r0].[Name], [s1].[RelationshipsTrunkRelationshipsRootId], [s1].[Id1], [s1].[Name], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsBranchId1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s1].[RelationshipsBranchId10], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s1].[RelationshipsBranchId11], [s1].[Id10], [s1].[Name0], [s1].[Name1], [s1].[Name2], [r1].[Name], [r31].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r31].[Id1], [r31].[Name], [r2].[Name], [r3].[Name], [r4].[Name], [r32].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r32].[Id1], [r32].[Name], [r5].[Name], [r6].[Name], [r7].[Name], [s2].[RelationshipsTrunkRelationshipsRootId], [s2].[Id1], [s2].[Name], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s2].[RelationshipsBranchId1], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s2].[RelationshipsBranchId10], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s2].[RelationshipsBranchId11], [s2].[Id10], [s2].[Name0], [s2].[Name1], [s2].[Name2], [r8].[Name], [r37].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r37].[Id1], [r37].[Name], [r9].[Name], [r10].[Name], [r11].[Name], [r38].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r38].[Id1], [r38].[Name], [r12].[Name], [r13].[Name], [s4].[RelationshipsRootId], [s4].[Id1], [s4].[Name], [s4].[RelationshipsTrunkRelationshipsRootId], [s4].[RelationshipsTrunkId1], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s4].[RelationshipsBranchRelationshipsTrunkId1], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s4].[RelationshipsBranchRelationshipsTrunkId10], [s4].[RelationshipsTrunkRelationshipsRootId0], [s4].[RelationshipsTrunkId10], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s4].[RelationshipsBranchRelationshipsTrunkId11], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [s4].[RelationshipsBranchRelationshipsTrunkId12], [s4].[RelationshipsTrunkRelationshipsRootId1], [s4].[RelationshipsTrunkId11], [s4].[Id10], [s4].[Name0], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s4].[RelationshipsBranchRelationshipsTrunkId13], [s4].[RelationshipsBranchId1], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s4].[RelationshipsBranchRelationshipsTrunkId100], [s4].[RelationshipsBranchId10], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s4].[RelationshipsBranchRelationshipsTrunkId110], [s4].[RelationshipsBranchId11], [s4].[Id100], [s4].[Name00], [s4].[Name1], [s4].[Name2], [s4].[Name3], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [s4].[RelationshipsBranchRelationshipsTrunkId14], [s4].[Id11], [s4].[Name4], [s4].[Name5], [s4].[Name6], [s4].[Name7], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [s4].[RelationshipsBranchRelationshipsTrunkId15], [s4].[Id12], [s4].[Name8], [s4].[Name9], [s4].[Name10], [s5].[RelationshipsTrunkRelationshipsRootId], [s5].[Id1], [s5].[Name], [s5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s5].[RelationshipsBranchId1], [s5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s5].[RelationshipsBranchId10], [s5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s5].[RelationshipsBranchId11], [s5].[Id10], [s5].[Name0], [s5].[Name1], [s5].[Name2], [r56].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r56].[Id1], [r56].[Name], [r57].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r57].[Id1], [r57].[Name], [s6].[RelationshipsTrunkRelationshipsRootId], [s6].[Id1], [s6].[Name], [s6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s6].[RelationshipsBranchId1], [s6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s6].[RelationshipsBranchId10], [s6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s6].[RelationshipsBranchId11], [s6].[Id10], [s6].[Name0], [s6].[Name1], [s6].[Name2], [r62].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r62].[Id1], [r62].[Name], [r63].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r63].[Id1], [r63].[Name] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN [Root_OptionalReferenceTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch] AS [r1] ON [r0].[RelationshipsRootId] = [r1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r3] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch] AS [r4] ON [r0].[RelationshipsRootId] = [r4].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r5] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r6] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk] AS [r7] ON [r].[Id] = [r7].[RelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch] AS [r8] ON [r7].[RelationshipsRootId] = [r8].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r9] ON [r8].[RelationshipsTrunkRelationshipsRootId] = [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r10] ON [r8].[RelationshipsTrunkRelationshipsRootId] = [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch] AS [r11] ON [r7].[RelationshipsRootId] = [r11].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r12] ON [r11].[RelationshipsTrunkRelationshipsRootId] = [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r13] ON [r11].[RelationshipsTrunkRelationshipsRootId] = [r13].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r14].[RelationshipsRootId], [r14].[Id1], [r14].[Name], [r15].[RelationshipsTrunkRelationshipsRootId], [r15].[RelationshipsTrunkId1], [r16].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r16].[RelationshipsBranchRelationshipsTrunkId1], [r17].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r17].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r18].[RelationshipsTrunkRelationshipsRootId] AS [RelationshipsTrunkRelationshipsRootId0], [r18].[RelationshipsTrunkId1] AS [RelationshipsTrunkId10], [r19].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r19].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r20].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [r20].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId12], [s].[RelationshipsTrunkRelationshipsRootId] AS [RelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsTrunkId1] AS [RelationshipsTrunkId11], [s].[Id1] AS [Id10], [s].[Name] AS [Name0], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId13], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s].[RelationshipsBranchRelationshipsTrunkId10] AS [RelationshipsBranchRelationshipsTrunkId100], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s].[RelationshipsBranchRelationshipsTrunkId11] AS [RelationshipsBranchRelationshipsTrunkId110], [s].[RelationshipsBranchId11], [s].[Id10] AS [Id100], [s].[Name0] AS [Name00], [s].[Name1], [s].[Name2], [r15].[Name] AS [Name3], [r25].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [r25].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId14], [r25].[Id1] AS [Id11], [r25].[Name] AS [Name4], [r16].[Name] AS [Name5], [r17].[Name] AS [Name6], [r18].[Name] AS [Name7], [r26].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [r26].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId15], [r26].[Id1] AS [Id12], [r26].[Name] AS [Name8], [r19].[Name] AS [Name9], [r20].[Name] AS [Name10] - FROM [RelationshipsRoot_CollectionTrunk] AS [r14] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch] AS [r15] ON [r14].[RelationshipsRootId] = [r15].[RelationshipsTrunkRelationshipsRootId] AND [r14].[Id1] = [r15].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r16] ON [r15].[RelationshipsTrunkRelationshipsRootId] = [r16].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r15].[RelationshipsTrunkId1] = [r16].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r17] ON [r15].[RelationshipsTrunkRelationshipsRootId] = [r17].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r15].[RelationshipsTrunkId1] = [r17].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch] AS [r18] ON [r14].[RelationshipsRootId] = [r18].[RelationshipsTrunkRelationshipsRootId] AND [r14].[Id1] = [r18].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r19] ON [r18].[RelationshipsTrunkRelationshipsRootId] = [r19].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r18].[RelationshipsTrunkId1] = [r19].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r20] ON [r18].[RelationshipsTrunkRelationshipsRootId] = [r20].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r18].[RelationshipsTrunkId1] = [r20].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN ( - SELECT [r21].[RelationshipsTrunkRelationshipsRootId], [r21].[RelationshipsTrunkId1], [r21].[Id1], [r21].[Name], [r22].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r22].[RelationshipsBranchRelationshipsTrunkId1], [r22].[RelationshipsBranchId1], [r23].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r23].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r23].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r24].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r24].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r24].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r24].[Id1] AS [Id10], [r24].[Name] AS [Name0], [r22].[Name] AS [Name1], [r23].[Name] AS [Name2] - FROM [RelationshipsRoot_CollectionTrunk_CollectionBranch] AS [r21] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r22] ON [r21].[RelationshipsTrunkRelationshipsRootId] = [r22].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r21].[RelationshipsTrunkId1] = [r22].[RelationshipsBranchRelationshipsTrunkId1] AND [r21].[Id1] = [r22].[RelationshipsBranchId1] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r23] ON [r21].[RelationshipsTrunkRelationshipsRootId] = [r23].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r21].[RelationshipsTrunkId1] = [r23].[RelationshipsBranchRelationshipsTrunkId1] AND [r21].[Id1] = [r23].[RelationshipsBranchId1] - LEFT JOIN [RelationshipsRoot_CollectionTrunk_CollectionBranch_CollectionLeaf] AS [r24] ON [r21].[RelationshipsTrunkRelationshipsRootId] = [r24].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r21].[RelationshipsTrunkId1] = [r24].[RelationshipsBranchRelationshipsTrunkId1] AND [r21].[Id1] = [r24].[RelationshipsBranchId1] - ) AS [s] ON [r14].[RelationshipsRootId] = [s].[RelationshipsTrunkRelationshipsRootId] AND [r14].[Id1] = [s].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r25] ON [r15].[RelationshipsTrunkRelationshipsRootId] = [r25].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r15].[RelationshipsTrunkId1] = [r25].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r26] ON [r18].[RelationshipsTrunkRelationshipsRootId] = [r26].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r18].[RelationshipsTrunkId1] = [r26].[RelationshipsBranchRelationshipsTrunkId1] -) AS [s0] ON [r].[Id] = [s0].[RelationshipsRootId] -LEFT JOIN ( - SELECT [r27].[RelationshipsTrunkRelationshipsRootId], [r27].[Id1], [r27].[Name], [r28].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r28].[RelationshipsBranchId1], [r29].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r29].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r30].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r30].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r30].[Id1] AS [Id10], [r30].[Name] AS [Name0], [r28].[Name] AS [Name1], [r29].[Name] AS [Name2] - FROM [Root_OptionalReferenceTrunk_CollectionBranch] AS [r27] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r28] ON [r27].[RelationshipsTrunkRelationshipsRootId] = [r28].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r27].[Id1] = [r28].[RelationshipsBranchId1] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r29] ON [r27].[RelationshipsTrunkRelationshipsRootId] = [r29].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r27].[Id1] = [r29].[RelationshipsBranchId1] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r30] ON [r27].[RelationshipsTrunkRelationshipsRootId] = [r30].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r27].[Id1] = [r30].[RelationshipsBranchId1] -) AS [s1] ON [r0].[RelationshipsRootId] = [s1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r31] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r31].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r32] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r32].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r33].[RelationshipsTrunkRelationshipsRootId], [r33].[Id1], [r33].[Name], [r34].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r34].[RelationshipsBranchId1], [r35].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r35].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r36].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r36].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r36].[Id1] AS [Id10], [r36].[Name] AS [Name0], [r34].[Name] AS [Name1], [r35].[Name] AS [Name2] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r33] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r34] ON [r33].[RelationshipsTrunkRelationshipsRootId] = [r34].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r33].[Id1] = [r34].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r35] ON [r33].[RelationshipsTrunkRelationshipsRootId] = [r35].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r33].[Id1] = [r35].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r36] ON [r33].[RelationshipsTrunkRelationshipsRootId] = [r36].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r33].[Id1] = [r36].[RelationshipsBranchId1] -) AS [s2] ON [r7].[RelationshipsRootId] = [s2].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r37] ON [r8].[RelationshipsTrunkRelationshipsRootId] = [r37].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r38] ON [r11].[RelationshipsTrunkRelationshipsRootId] = [r38].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r39].[RelationshipsRootId], [r39].[Id1], [r39].[Name], [r40].[RelationshipsTrunkRelationshipsRootId], [r40].[RelationshipsTrunkId1], [r41].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r41].[RelationshipsBranchRelationshipsTrunkId1], [r42].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r42].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r43].[RelationshipsTrunkRelationshipsRootId] AS [RelationshipsTrunkRelationshipsRootId0], [r43].[RelationshipsTrunkId1] AS [RelationshipsTrunkId10], [r44].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r44].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r45].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [r45].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId12], [s3].[RelationshipsTrunkRelationshipsRootId] AS [RelationshipsTrunkRelationshipsRootId1], [s3].[RelationshipsTrunkId1] AS [RelationshipsTrunkId11], [s3].[Id1] AS [Id10], [s3].[Name] AS [Name0], [s3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s3].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId13], [s3].[RelationshipsBranchId1], [s3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s3].[RelationshipsBranchRelationshipsTrunkId10] AS [RelationshipsBranchRelationshipsTrunkId100], [s3].[RelationshipsBranchId10], [s3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s3].[RelationshipsBranchRelationshipsTrunkId11] AS [RelationshipsBranchRelationshipsTrunkId110], [s3].[RelationshipsBranchId11], [s3].[Id10] AS [Id100], [s3].[Name0] AS [Name00], [s3].[Name1], [s3].[Name2], [r40].[Name] AS [Name3], [r50].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [r50].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId14], [r50].[Id1] AS [Id11], [r50].[Name] AS [Name4], [r41].[Name] AS [Name5], [r42].[Name] AS [Name6], [r43].[Name] AS [Name7], [r51].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [r51].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId15], [r51].[Id1] AS [Id12], [r51].[Name] AS [Name8], [r44].[Name] AS [Name9], [r45].[Name] AS [Name10] - FROM [RelationshipsRoot_CollectionTrunk] AS [r39] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch] AS [r40] ON [r39].[RelationshipsRootId] = [r40].[RelationshipsTrunkRelationshipsRootId] AND [r39].[Id1] = [r40].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r41] ON [r40].[RelationshipsTrunkRelationshipsRootId] = [r41].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r40].[RelationshipsTrunkId1] = [r41].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r42] ON [r40].[RelationshipsTrunkRelationshipsRootId] = [r42].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r40].[RelationshipsTrunkId1] = [r42].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch] AS [r43] ON [r39].[RelationshipsRootId] = [r43].[RelationshipsTrunkRelationshipsRootId] AND [r39].[Id1] = [r43].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r44] ON [r43].[RelationshipsTrunkRelationshipsRootId] = [r44].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r43].[RelationshipsTrunkId1] = [r44].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r45] ON [r43].[RelationshipsTrunkRelationshipsRootId] = [r45].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r43].[RelationshipsTrunkId1] = [r45].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN ( - SELECT [r46].[RelationshipsTrunkRelationshipsRootId], [r46].[RelationshipsTrunkId1], [r46].[Id1], [r46].[Name], [r47].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r47].[RelationshipsBranchRelationshipsTrunkId1], [r47].[RelationshipsBranchId1], [r48].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r48].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r48].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r49].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r49].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r49].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r49].[Id1] AS [Id10], [r49].[Name] AS [Name0], [r47].[Name] AS [Name1], [r48].[Name] AS [Name2] - FROM [RelationshipsRoot_CollectionTrunk_CollectionBranch] AS [r46] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r47] ON [r46].[RelationshipsTrunkRelationshipsRootId] = [r47].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r46].[RelationshipsTrunkId1] = [r47].[RelationshipsBranchRelationshipsTrunkId1] AND [r46].[Id1] = [r47].[RelationshipsBranchId1] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r48] ON [r46].[RelationshipsTrunkRelationshipsRootId] = [r48].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r46].[RelationshipsTrunkId1] = [r48].[RelationshipsBranchRelationshipsTrunkId1] AND [r46].[Id1] = [r48].[RelationshipsBranchId1] - LEFT JOIN [RelationshipsRoot_CollectionTrunk_CollectionBranch_CollectionLeaf] AS [r49] ON [r46].[RelationshipsTrunkRelationshipsRootId] = [r49].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r46].[RelationshipsTrunkId1] = [r49].[RelationshipsBranchRelationshipsTrunkId1] AND [r46].[Id1] = [r49].[RelationshipsBranchId1] - ) AS [s3] ON [r39].[RelationshipsRootId] = [s3].[RelationshipsTrunkRelationshipsRootId] AND [r39].[Id1] = [s3].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r50] ON [r40].[RelationshipsTrunkRelationshipsRootId] = [r50].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r40].[RelationshipsTrunkId1] = [r50].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r51] ON [r43].[RelationshipsTrunkRelationshipsRootId] = [r51].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r43].[RelationshipsTrunkId1] = [r51].[RelationshipsBranchRelationshipsTrunkId1] -) AS [s4] ON [r].[Id] = [s4].[RelationshipsRootId] -LEFT JOIN ( - SELECT [r52].[RelationshipsTrunkRelationshipsRootId], [r52].[Id1], [r52].[Name], [r53].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r53].[RelationshipsBranchId1], [r54].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r54].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r55].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r55].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r55].[Id1] AS [Id10], [r55].[Name] AS [Name0], [r53].[Name] AS [Name1], [r54].[Name] AS [Name2] - FROM [Root_OptionalReferenceTrunk_CollectionBranch] AS [r52] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r53] ON [r52].[RelationshipsTrunkRelationshipsRootId] = [r53].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r52].[Id1] = [r53].[RelationshipsBranchId1] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r54] ON [r52].[RelationshipsTrunkRelationshipsRootId] = [r54].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r52].[Id1] = [r54].[RelationshipsBranchId1] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r55] ON [r52].[RelationshipsTrunkRelationshipsRootId] = [r55].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r52].[Id1] = [r55].[RelationshipsBranchId1] -) AS [s5] ON [r0].[RelationshipsRootId] = [s5].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r56] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r56].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r57] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r57].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r58].[RelationshipsTrunkRelationshipsRootId], [r58].[Id1], [r58].[Name], [r59].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r59].[RelationshipsBranchId1], [r60].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r60].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r61].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r61].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r61].[Id1] AS [Id10], [r61].[Name] AS [Name0], [r59].[Name] AS [Name1], [r60].[Name] AS [Name2] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r58] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r59] ON [r58].[RelationshipsTrunkRelationshipsRootId] = [r59].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r58].[Id1] = [r59].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r60] ON [r58].[RelationshipsTrunkRelationshipsRootId] = [r60].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r58].[Id1] = [r60].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r61] ON [r58].[RelationshipsTrunkRelationshipsRootId] = [r61].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r58].[Id1] = [r61].[RelationshipsBranchId1] -) AS [s6] ON [r7].[RelationshipsRootId] = [s6].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r62] ON [r8].[RelationshipsTrunkRelationshipsRootId] = [r62].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r63] ON [r11].[RelationshipsTrunkRelationshipsRootId] = [r63].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r7].[RelationshipsRootId], [r8].[RelationshipsTrunkRelationshipsRootId], [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[RelationshipsTrunkRelationshipsRootId], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r13].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsRootId], [s0].[Id1], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsTrunkId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchRelationshipsTrunkId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchRelationshipsTrunkId10], [s0].[RelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsTrunkId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchRelationshipsTrunkId11], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [s0].[RelationshipsBranchRelationshipsTrunkId12], [s0].[RelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsTrunkId11], [s0].[Id10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s0].[RelationshipsBranchRelationshipsTrunkId13], [s0].[RelationshipsBranchId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s0].[RelationshipsBranchRelationshipsTrunkId100], [s0].[RelationshipsBranchId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s0].[RelationshipsBranchRelationshipsTrunkId110], [s0].[RelationshipsBranchId11], [s0].[Id100], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [s0].[RelationshipsBranchRelationshipsTrunkId14], [s0].[Id11], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [s0].[RelationshipsBranchRelationshipsTrunkId15], [s0].[Id12], [s1].[RelationshipsTrunkRelationshipsRootId], [s1].[Id1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsBranchId1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s1].[RelationshipsBranchId10], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s1].[RelationshipsBranchId11], [s1].[Id10], [r31].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r31].[Id1], [r32].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r32].[Id1], [s2].[RelationshipsTrunkRelationshipsRootId], [s2].[Id1], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s2].[RelationshipsBranchId1], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s2].[RelationshipsBranchId10], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s2].[RelationshipsBranchId11], [s2].[Id10], [r37].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r37].[Id1], [r38].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r38].[Id1], [s4].[RelationshipsRootId], [s4].[Id1], [s4].[RelationshipsTrunkRelationshipsRootId], [s4].[RelationshipsTrunkId1], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s4].[RelationshipsBranchRelationshipsTrunkId1], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s4].[RelationshipsBranchRelationshipsTrunkId10], [s4].[RelationshipsTrunkRelationshipsRootId0], [s4].[RelationshipsTrunkId10], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s4].[RelationshipsBranchRelationshipsTrunkId11], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [s4].[RelationshipsBranchRelationshipsTrunkId12], [s4].[RelationshipsTrunkRelationshipsRootId1], [s4].[RelationshipsTrunkId11], [s4].[Id10], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s4].[RelationshipsBranchRelationshipsTrunkId13], [s4].[RelationshipsBranchId1], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s4].[RelationshipsBranchRelationshipsTrunkId100], [s4].[RelationshipsBranchId10], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s4].[RelationshipsBranchRelationshipsTrunkId110], [s4].[RelationshipsBranchId11], [s4].[Id100], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [s4].[RelationshipsBranchRelationshipsTrunkId14], [s4].[Id11], [s4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [s4].[RelationshipsBranchRelationshipsTrunkId15], [s4].[Id12], [s5].[RelationshipsTrunkRelationshipsRootId], [s5].[Id1], [s5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s5].[RelationshipsBranchId1], [s5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s5].[RelationshipsBranchId10], [s5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s5].[RelationshipsBranchId11], [s5].[Id10], [r56].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r56].[Id1], [r57].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r57].[Id1], [s6].[RelationshipsTrunkRelationshipsRootId], [s6].[Id1], [s6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s6].[RelationshipsBranchId1], [s6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s6].[RelationshipsBranchId10], [s6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s6].[RelationshipsBranchId11], [s6].[Id10], [r62].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r62].[Id1], [r63].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] +SELECT [r].[Id], [r].[OptionalRelated_OptionalNested_Id], [r].[OptionalRelated_OptionalNested_Int], [r].[OptionalRelated_OptionalNested_Name], [r].[OptionalRelated_OptionalNested_String] +FROM [RootEntity] AS [r] """); } } - public override async Task Select_trunk_and_branch_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_trunk_and_branch_duplicated(async, queryTrackingBehavior); + #endregion Non-collection - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r0].[RelationshipsRootId], [r0].[Name], [r].[Id], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchId11], [s].[Id10], [s].[Name0], [s].[Name1], [s].[Name2], [r1].[Name], [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[Id1], [r11].[Name], [r2].[Name], [r3].[Name], [r4].[Name], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r12].[Id1], [r12].[Name], [r5].[Name], [r6].[Name], [r13].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r13].[Id1], [r13].[Name], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[Id1], [s0].[Name], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchId11], [s0].[Id10], [s0].[Name0], [s0].[Name1], [s0].[Name2], [r18].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r18].[Id1], [r18].[Name], [r19].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r19].[Id1], [r19].[Name], [r20].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r20].[Id1], [r20].[Name] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN [Root_OptionalReferenceTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch] AS [r1] ON [r0].[RelationshipsRootId] = [r1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r3] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch] AS [r4] ON [r0].[RelationshipsRootId] = [r4].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r5] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r6] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r7].[RelationshipsTrunkRelationshipsRootId], [r7].[Id1], [r7].[Name], [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r8].[RelationshipsBranchId1], [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r9].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r10].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r10].[Id1] AS [Id10], [r10].[Name] AS [Name0], [r8].[Name] AS [Name1], [r9].[Name] AS [Name2] - FROM [Root_OptionalReferenceTrunk_CollectionBranch] AS [r7] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r8] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r7].[Id1] = [r8].[RelationshipsBranchId1] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r9] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r7].[Id1] = [r9].[RelationshipsBranchId1] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r10] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r7].[Id1] = [r10].[RelationshipsBranchId1] -) AS [s] ON [r0].[RelationshipsRootId] = [s].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r11] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r12] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r13] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r13].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r14].[RelationshipsTrunkRelationshipsRootId], [r14].[Id1], [r14].[Name], [r15].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r15].[RelationshipsBranchId1], [r16].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r16].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r17].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r17].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r17].[Id1] AS [Id10], [r17].[Name] AS [Name0], [r15].[Name] AS [Name1], [r16].[Name] AS [Name2] - FROM [Root_OptionalReferenceTrunk_CollectionBranch] AS [r14] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r15] ON [r14].[RelationshipsTrunkRelationshipsRootId] = [r15].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r14].[Id1] = [r15].[RelationshipsBranchId1] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r16] ON [r14].[RelationshipsTrunkRelationshipsRootId] = [r16].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r14].[Id1] = [r16].[RelationshipsBranchId1] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r17] ON [r14].[RelationshipsTrunkRelationshipsRootId] = [r17].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r14].[Id1] = [r17].[RelationshipsBranchId1] -) AS [s0] ON [r0].[RelationshipsRootId] = [s0].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r18] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r18].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r19] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r19].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r20] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r20].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchId11], [s].[Id10], [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[Id1], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r12].[Id1], [r13].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r13].[Id1], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[Id1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchId11], [s0].[Id10], [r18].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r18].[Id1], [r19].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r19].[Id1], [r20].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -"""); - } - } + #region Collection - public override async Task Select_trunk_and_trunk_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_trunk_and_trunk_duplicated(async, queryTrackingBehavior); + await base.Select_related_collection(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r0].[RelationshipsRootId], [r0].[Name], [r].[Id], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchId11], [s].[Id10], [s].[Name0], [s].[Name1], [s].[Name2], [r1].[Name], [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[Id1], [r11].[Name], [r2].[Name], [r3].[Name], [r4].[Name], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r12].[Id1], [r12].[Name], [r5].[Name], [r6].[Name], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[Id1], [s0].[Name], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchId11], [s0].[Id10], [s0].[Name0], [s0].[Name1], [s0].[Name2], [r17].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r17].[Id1], [r17].[Name], [r18].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r18].[Id1], [r18].[Name] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN [Root_RequiredReferenceTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch] AS [r1] ON [r0].[RelationshipsRootId] = [r1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r3] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch] AS [r4] ON [r0].[RelationshipsRootId] = [r4].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r5] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r6] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r7].[RelationshipsTrunkRelationshipsRootId], [r7].[Id1], [r7].[Name], [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r8].[RelationshipsBranchId1], [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r9].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r10].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r10].[Id1] AS [Id10], [r10].[Name] AS [Name0], [r8].[Name] AS [Name1], [r9].[Name] AS [Name2] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r7] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r8] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r7].[Id1] = [r8].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r9] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r7].[Id1] = [r9].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r10] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r7].[Id1] = [r10].[RelationshipsBranchId1] -) AS [s] ON [r0].[RelationshipsRootId] = [s].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r11] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r12] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r13].[RelationshipsTrunkRelationshipsRootId], [r13].[Id1], [r13].[Name], [r14].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r14].[RelationshipsBranchId1], [r15].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r15].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r16].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r16].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r16].[Id1] AS [Id10], [r16].[Name] AS [Name0], [r14].[Name] AS [Name1], [r15].[Name] AS [Name2] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r13] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r14] ON [r13].[RelationshipsTrunkRelationshipsRootId] = [r14].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r13].[Id1] = [r14].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r15] ON [r13].[RelationshipsTrunkRelationshipsRootId] = [r15].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r13].[Id1] = [r15].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r16] ON [r13].[RelationshipsTrunkRelationshipsRootId] = [r16].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r13].[Id1] = [r16].[RelationshipsBranchId1] -) AS [s0] ON [r0].[RelationshipsRootId] = [s0].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r17] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r17].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r18] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r18].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchId11], [s].[Id10], [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[Id1], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r12].[Id1], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[Id1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchId11], [s0].[Id10], [r17].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r17].[Id1], [r18].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] +SELECT [r].[Id], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[Id0], [s].[Int0], [s].[Name0], [s].[String0], [s].[OptionalNested_Id], [s].[OptionalNested_Int], [s].[OptionalNested_Name], [s].[OptionalNested_String], [s].[RequiredNested_Id], [s].[RequiredNested_Int], [s].[RequiredNested_Name], [s].[RequiredNested_String] +FROM [RootEntity] AS [r] +LEFT JOIN ( + SELECT [r0].[RootEntityId], [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String], [r1].[RelatedTypeRootEntityId], [r1].[RelatedTypeId], [r1].[Id] AS [Id0], [r1].[Int] AS [Int0], [r1].[Name] AS [Name0], [r1].[String] AS [String0], [r0].[OptionalNested_Id], [r0].[OptionalNested_Int], [r0].[OptionalNested_Name], [r0].[OptionalNested_String], [r0].[RequiredNested_Id], [r0].[RequiredNested_Int], [r0].[RequiredNested_Name], [r0].[RequiredNested_String] + FROM [RelatedCollection] AS [r0] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r1] ON [r0].[RootEntityId] = [r1].[RelatedTypeRootEntityId] AND [r0].[Id] = [r1].[RelatedTypeId] +) AS [s] ON [r].[Id] = [s].[RootEntityId] +ORDER BY [r].[Id], [s].[RootEntityId], [s].[Id], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId] """); } } - public override async Task Select_leaf_trunk_root(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_leaf_trunk_root(async, queryTrackingBehavior); + await base.Select_required_related_nested_collection(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[Name], [r0].[RelationshipsRootId], [r0].[Name], [r].[Id], [r1].[RelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r7].[RelationshipsRootId], [r8].[RelationshipsTrunkRelationshipsRootId], [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[RelationshipsTrunkRelationshipsRootId], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r13].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchId11], [s].[Id10], [s].[Name0], [s].[Name1], [s].[Name2], [r3].[Name], [r18].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r18].[Id1], [r18].[Name], [r4].[Name], [r5].[Name], [r1].[Name], [r19].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r19].[Id1], [r19].[Name], [r6].[Name], [r].[Name], [r].[OptionalReferenceTrunkId], [r].[RequiredReferenceTrunkId], [s1].[RelationshipsRootId], [s1].[Id1], [s1].[Name], [s1].[RelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsTrunkId1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsBranchRelationshipsTrunkId1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s1].[RelationshipsBranchRelationshipsTrunkId10], [s1].[RelationshipsTrunkRelationshipsRootId0], [s1].[RelationshipsTrunkId10], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s1].[RelationshipsBranchRelationshipsTrunkId11], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [s1].[RelationshipsBranchRelationshipsTrunkId12], [s1].[RelationshipsTrunkRelationshipsRootId1], [s1].[RelationshipsTrunkId11], [s1].[Id10], [s1].[Name0], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s1].[RelationshipsBranchRelationshipsTrunkId13], [s1].[RelationshipsBranchId1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s1].[RelationshipsBranchRelationshipsTrunkId100], [s1].[RelationshipsBranchId10], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s1].[RelationshipsBranchRelationshipsTrunkId110], [s1].[RelationshipsBranchId11], [s1].[Id100], [s1].[Name00], [s1].[Name1], [s1].[Name2], [s1].[Name3], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [s1].[RelationshipsBranchRelationshipsTrunkId14], [s1].[Id11], [s1].[Name4], [s1].[Name5], [s1].[Name6], [s1].[Name7], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [s1].[RelationshipsBranchRelationshipsTrunkId15], [s1].[Id12], [s1].[Name8], [s1].[Name9], [s1].[Name10], [r7].[Name], [s2].[RelationshipsTrunkRelationshipsRootId], [s2].[Id1], [s2].[Name], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s2].[RelationshipsBranchId1], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s2].[RelationshipsBranchId10], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s2].[RelationshipsBranchId11], [s2].[Id10], [s2].[Name0], [s2].[Name1], [s2].[Name2], [r8].[Name], [r37].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r37].[Id1], [r37].[Name], [r9].[Name], [r10].[Name], [r11].[Name], [r38].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r38].[Id1], [r38].[Name], [r12].[Name], [r13].[Name], [s3].[RelationshipsTrunkRelationshipsRootId], [s3].[Id1], [s3].[Name], [s3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s3].[RelationshipsBranchId1], [s3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s3].[RelationshipsBranchId10], [s3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s3].[RelationshipsBranchId11], [s3].[Id10], [s3].[Name0], [s3].[Name1], [s3].[Name2], [r43].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r43].[Id1], [r43].[Name], [r44].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r44].[Id1], [r44].[Name] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN [Root_RequiredReferenceTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch] AS [r1] ON [r0].[RelationshipsRootId] = [r1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch] AS [r3] ON [r0].[RelationshipsRootId] = [r3].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r4] ON [r3].[RelationshipsTrunkRelationshipsRootId] = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r5] ON [r3].[RelationshipsTrunkRelationshipsRootId] = [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r6] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk] AS [r7] ON [r].[Id] = [r7].[RelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch] AS [r8] ON [r7].[RelationshipsRootId] = [r8].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r9] ON [r8].[RelationshipsTrunkRelationshipsRootId] = [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r10] ON [r8].[RelationshipsTrunkRelationshipsRootId] = [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch] AS [r11] ON [r7].[RelationshipsRootId] = [r11].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r12] ON [r11].[RelationshipsTrunkRelationshipsRootId] = [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r13] ON [r11].[RelationshipsTrunkRelationshipsRootId] = [r13].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r14].[RelationshipsTrunkRelationshipsRootId], [r14].[Id1], [r14].[Name], [r15].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r15].[RelationshipsBranchId1], [r16].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r16].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r17].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r17].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r17].[Id1] AS [Id10], [r17].[Name] AS [Name0], [r15].[Name] AS [Name1], [r16].[Name] AS [Name2] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r14] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r15] ON [r14].[RelationshipsTrunkRelationshipsRootId] = [r15].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r14].[Id1] = [r15].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r16] ON [r14].[RelationshipsTrunkRelationshipsRootId] = [r16].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r14].[Id1] = [r16].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r17] ON [r14].[RelationshipsTrunkRelationshipsRootId] = [r17].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r14].[Id1] = [r17].[RelationshipsBranchId1] -) AS [s] ON [r0].[RelationshipsRootId] = [s].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r18] ON [r3].[RelationshipsTrunkRelationshipsRootId] = [r18].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r19] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r19].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r20].[RelationshipsRootId], [r20].[Id1], [r20].[Name], [r21].[RelationshipsTrunkRelationshipsRootId], [r21].[RelationshipsTrunkId1], [r22].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r22].[RelationshipsBranchRelationshipsTrunkId1], [r23].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r23].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r24].[RelationshipsTrunkRelationshipsRootId] AS [RelationshipsTrunkRelationshipsRootId0], [r24].[RelationshipsTrunkId1] AS [RelationshipsTrunkId10], [r25].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r25].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r26].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [r26].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId12], [s0].[RelationshipsTrunkRelationshipsRootId] AS [RelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsTrunkId1] AS [RelationshipsTrunkId11], [s0].[Id1] AS [Id10], [s0].[Name] AS [Name0], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s0].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId13], [s0].[RelationshipsBranchId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s0].[RelationshipsBranchRelationshipsTrunkId10] AS [RelationshipsBranchRelationshipsTrunkId100], [s0].[RelationshipsBranchId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s0].[RelationshipsBranchRelationshipsTrunkId11] AS [RelationshipsBranchRelationshipsTrunkId110], [s0].[RelationshipsBranchId11], [s0].[Id10] AS [Id100], [s0].[Name0] AS [Name00], [s0].[Name1], [s0].[Name2], [r21].[Name] AS [Name3], [r31].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [r31].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId14], [r31].[Id1] AS [Id11], [r31].[Name] AS [Name4], [r22].[Name] AS [Name5], [r23].[Name] AS [Name6], [r24].[Name] AS [Name7], [r32].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [r32].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId15], [r32].[Id1] AS [Id12], [r32].[Name] AS [Name8], [r25].[Name] AS [Name9], [r26].[Name] AS [Name10] - FROM [RelationshipsRoot_CollectionTrunk] AS [r20] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch] AS [r21] ON [r20].[RelationshipsRootId] = [r21].[RelationshipsTrunkRelationshipsRootId] AND [r20].[Id1] = [r21].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r22] ON [r21].[RelationshipsTrunkRelationshipsRootId] = [r22].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r21].[RelationshipsTrunkId1] = [r22].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r23] ON [r21].[RelationshipsTrunkRelationshipsRootId] = [r23].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r21].[RelationshipsTrunkId1] = [r23].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch] AS [r24] ON [r20].[RelationshipsRootId] = [r24].[RelationshipsTrunkRelationshipsRootId] AND [r20].[Id1] = [r24].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r25] ON [r24].[RelationshipsTrunkRelationshipsRootId] = [r25].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r24].[RelationshipsTrunkId1] = [r25].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r26] ON [r24].[RelationshipsTrunkRelationshipsRootId] = [r26].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r24].[RelationshipsTrunkId1] = [r26].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN ( - SELECT [r27].[RelationshipsTrunkRelationshipsRootId], [r27].[RelationshipsTrunkId1], [r27].[Id1], [r27].[Name], [r28].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r28].[RelationshipsBranchRelationshipsTrunkId1], [r28].[RelationshipsBranchId1], [r29].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r29].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r29].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r30].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r30].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r30].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r30].[Id1] AS [Id10], [r30].[Name] AS [Name0], [r28].[Name] AS [Name1], [r29].[Name] AS [Name2] - FROM [RelationshipsRoot_CollectionTrunk_CollectionBranch] AS [r27] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r28] ON [r27].[RelationshipsTrunkRelationshipsRootId] = [r28].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r27].[RelationshipsTrunkId1] = [r28].[RelationshipsBranchRelationshipsTrunkId1] AND [r27].[Id1] = [r28].[RelationshipsBranchId1] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r29] ON [r27].[RelationshipsTrunkRelationshipsRootId] = [r29].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r27].[RelationshipsTrunkId1] = [r29].[RelationshipsBranchRelationshipsTrunkId1] AND [r27].[Id1] = [r29].[RelationshipsBranchId1] - LEFT JOIN [RelationshipsRoot_CollectionTrunk_CollectionBranch_CollectionLeaf] AS [r30] ON [r27].[RelationshipsTrunkRelationshipsRootId] = [r30].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r27].[RelationshipsTrunkId1] = [r30].[RelationshipsBranchRelationshipsTrunkId1] AND [r27].[Id1] = [r30].[RelationshipsBranchId1] - ) AS [s0] ON [r20].[RelationshipsRootId] = [s0].[RelationshipsTrunkRelationshipsRootId] AND [r20].[Id1] = [s0].[RelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r31] ON [r21].[RelationshipsTrunkRelationshipsRootId] = [r31].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r21].[RelationshipsTrunkId1] = [r31].[RelationshipsBranchRelationshipsTrunkId1] - LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r32] ON [r24].[RelationshipsTrunkRelationshipsRootId] = [r32].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r24].[RelationshipsTrunkId1] = [r32].[RelationshipsBranchRelationshipsTrunkId1] -) AS [s1] ON [r].[Id] = [s1].[RelationshipsRootId] -LEFT JOIN ( - SELECT [r33].[RelationshipsTrunkRelationshipsRootId], [r33].[Id1], [r33].[Name], [r34].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r34].[RelationshipsBranchId1], [r35].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r35].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r36].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r36].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r36].[Id1] AS [Id10], [r36].[Name] AS [Name0], [r34].[Name] AS [Name1], [r35].[Name] AS [Name2] - FROM [Root_OptionalReferenceTrunk_CollectionBranch] AS [r33] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r34] ON [r33].[RelationshipsTrunkRelationshipsRootId] = [r34].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r33].[Id1] = [r34].[RelationshipsBranchId1] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r35] ON [r33].[RelationshipsTrunkRelationshipsRootId] = [r35].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r33].[Id1] = [r35].[RelationshipsBranchId1] - LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r36] ON [r33].[RelationshipsTrunkRelationshipsRootId] = [r36].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r33].[Id1] = [r36].[RelationshipsBranchId1] -) AS [s2] ON [r7].[RelationshipsRootId] = [s2].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r37] ON [r8].[RelationshipsTrunkRelationshipsRootId] = [r37].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r38] ON [r11].[RelationshipsTrunkRelationshipsRootId] = [r38].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r39].[RelationshipsTrunkRelationshipsRootId], [r39].[Id1], [r39].[Name], [r40].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r40].[RelationshipsBranchId1], [r41].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r41].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r42].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r42].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r42].[Id1] AS [Id10], [r42].[Name] AS [Name0], [r40].[Name] AS [Name1], [r41].[Name] AS [Name2] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r39] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r40] ON [r39].[RelationshipsTrunkRelationshipsRootId] = [r40].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r39].[Id1] = [r40].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r41] ON [r39].[RelationshipsTrunkRelationshipsRootId] = [r41].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r39].[Id1] = [r41].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r42] ON [r39].[RelationshipsTrunkRelationshipsRootId] = [r42].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r39].[Id1] = [r42].[RelationshipsBranchId1] -) AS [s3] ON [r0].[RelationshipsRootId] = [s3].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r43] ON [r3].[RelationshipsTrunkRelationshipsRootId] = [r43].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r44] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r44].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r7].[RelationshipsRootId], [r8].[RelationshipsTrunkRelationshipsRootId], [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[RelationshipsTrunkRelationshipsRootId], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r13].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchId11], [s].[Id10], [r18].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r18].[Id1], [r19].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r19].[Id1], [s1].[RelationshipsRootId], [s1].[Id1], [s1].[RelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsTrunkId1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsBranchRelationshipsTrunkId1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s1].[RelationshipsBranchRelationshipsTrunkId10], [s1].[RelationshipsTrunkRelationshipsRootId0], [s1].[RelationshipsTrunkId10], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s1].[RelationshipsBranchRelationshipsTrunkId11], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [s1].[RelationshipsBranchRelationshipsTrunkId12], [s1].[RelationshipsTrunkRelationshipsRootId1], [s1].[RelationshipsTrunkId11], [s1].[Id10], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId3], [s1].[RelationshipsBranchRelationshipsTrunkId13], [s1].[RelationshipsBranchId1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId00], [s1].[RelationshipsBranchRelationshipsTrunkId100], [s1].[RelationshipsBranchId10], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId10], [s1].[RelationshipsBranchRelationshipsTrunkId110], [s1].[RelationshipsBranchId11], [s1].[Id100], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId4], [s1].[RelationshipsBranchRelationshipsTrunkId14], [s1].[Id11], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId5], [s1].[RelationshipsBranchRelationshipsTrunkId15], [s1].[Id12], [s2].[RelationshipsTrunkRelationshipsRootId], [s2].[Id1], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s2].[RelationshipsBranchId1], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s2].[RelationshipsBranchId10], [s2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s2].[RelationshipsBranchId11], [s2].[Id10], [r37].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r37].[Id1], [r38].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r38].[Id1], [s3].[RelationshipsTrunkRelationshipsRootId], [s3].[Id1], [s3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s3].[RelationshipsBranchId1], [s3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s3].[RelationshipsBranchId10], [s3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s3].[RelationshipsBranchId11], [s3].[Id10], [r43].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r43].[Id1], [r44].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] +SELECT [r].[Id], [r0].[RelatedTypeRootEntityId], [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r0] ON [r].[Id] = [r0].[RelatedTypeRootEntityId] +ORDER BY [r].[Id], [r0].[RelatedTypeRootEntityId] """); } } - public override async Task Select_multiple_branch_leaf(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_multiple_branch_leaf(async, queryTrackingBehavior); + await base.Select_optional_related_nested_collection(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r].[Id], [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[Name], [r0].[RelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[Id1], [r4].[Name], [r2].[Name], [r3].[Name], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r5].[Id1], [r5].[Name], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchId11], [s].[Id10], [s].[Name0], [s].[Name1], [s].[Name2] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN [Root_RequiredReferenceTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch] AS [r1] ON [r0].[RelationshipsRootId] = [r1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r3] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r4] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r5] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r6].[RelationshipsTrunkRelationshipsRootId], [r6].[Id1], [r6].[Name], [r7].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r7].[RelationshipsBranchId1], [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r8].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r9].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r9].[Id1] AS [Id10], [r9].[Name] AS [Name0], [r7].[Name] AS [Name1], [r8].[Name] AS [Name2] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r6] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r7] ON [r6].[RelationshipsTrunkRelationshipsRootId] = [r7].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r6].[Id1] = [r7].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r8] ON [r6].[RelationshipsTrunkRelationshipsRootId] = [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r6].[Id1] = [r8].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r9] ON [r6].[RelationshipsTrunkRelationshipsRootId] = [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r6].[Id1] = [r9].[RelationshipsBranchId1] -) AS [s] ON [r0].[RelationshipsRootId] = [s].[RelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[Id1], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r5].[Id1], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchId11] +SELECT [r].[Id], [o].[RelatedTypeRootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o] ON CASE + WHEN [r].[OptionalRelated_Id] IS NOT NULL AND [r].[OptionalRelated_Int] IS NOT NULL AND [r].[OptionalRelated_Name] IS NOT NULL AND [r].[OptionalRelated_String] IS NOT NULL THEN [r].[Id] +END = [o].[RelatedTypeRootEntityId] +ORDER BY [r].[Id], [o].[RelatedTypeRootEntityId] """); } } - #endregion Multiple - - #region Subquery - - public override async Task Select_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task SelectMany_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_subquery_root_set_required_trunk_FirstOrDefault_branch(async, queryTrackingBehavior); + await base.SelectMany_related_collection(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [s].[RelationshipsTrunkRelationshipsRootId], [s].[Name], [r].[Id], [s].[Id], [s].[RelationshipsRootId], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r5].[Id1], [r5].[Name], [s].[Name0], [s].[Name1] -FROM [RelationshipsRoot] AS [r] -OUTER APPLY ( - SELECT TOP(1) [r2].[RelationshipsTrunkRelationshipsRootId], [r2].[Name], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[Name] AS [Name0], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r4].[Name] AS [Name1], [r0].[Id], [r1].[RelationshipsRootId] - FROM [RelationshipsRoot] AS [r0] - LEFT JOIN [Root_RequiredReferenceTrunk] AS [r1] ON [r0].[Id] = [r1].[RelationshipsRootId] - LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch] AS [r2] ON [r1].[RelationshipsRootId] = [r2].[RelationshipsTrunkRelationshipsRootId] - LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r3] ON [r2].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] - LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r4] ON [r2].[RelationshipsTrunkRelationshipsRootId] = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] - ORDER BY [r0].[Id] -) AS [s] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r5] ON [s].[RelationshipsTrunkRelationshipsRootId] = [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [s].[Id], [s].[RelationshipsRootId], [s].[RelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] +SELECT [r0].[RootEntityId], [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String], [r].[Id], [r1].[RelatedTypeRootEntityId], [r1].[RelatedTypeId], [r1].[Id], [r1].[Int], [r1].[Name], [r1].[String], [r0].[OptionalNested_Id], [r0].[OptionalNested_Int], [r0].[OptionalNested_Name], [r0].[OptionalNested_String], [r0].[RequiredNested_Id], [r0].[RequiredNested_Int], [r0].[RequiredNested_Name], [r0].[RequiredNested_String] +FROM [RootEntity] AS [r] +INNER JOIN [RelatedCollection] AS [r0] ON [r].[Id] = [r0].[RootEntityId] +LEFT JOIN [RelatedCollection_NestedCollection] AS [r1] ON [r0].[RootEntityId] = [r1].[RelatedTypeRootEntityId] AND [r0].[Id] = [r1].[RelatedTypeId] +ORDER BY [r].[Id], [r0].[RootEntityId], [r0].[Id], [r1].[RelatedTypeRootEntityId], [r1].[RelatedTypeId] """); } } - public override async Task Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task SelectMany_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(async, queryTrackingBehavior); + await base.SelectMany_required_related_nested_collection(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [s].[RelationshipsTrunkRelationshipsRootId], [s].[Name], [r].[Id], [s].[Id], [s].[RelationshipsRootId], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r5].[Id1], [r5].[Name], [s].[Name0], [s].[Name1] -FROM [RelationshipsRoot] AS [r] -OUTER APPLY ( - SELECT TOP(1) [r2].[RelationshipsTrunkRelationshipsRootId], [r2].[Name], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[Name] AS [Name0], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r4].[Name] AS [Name1], [r0].[Id], [r1].[RelationshipsRootId] - FROM [RelationshipsRoot] AS [r0] - LEFT JOIN [Root_OptionalReferenceTrunk] AS [r1] ON [r0].[Id] = [r1].[RelationshipsRootId] - LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch] AS [r2] ON [r1].[RelationshipsRootId] = [r2].[RelationshipsTrunkRelationshipsRootId] - LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r3] ON [r2].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] - LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r4] ON [r2].[RelationshipsTrunkRelationshipsRootId] = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] - ORDER BY [r0].[Id] -) AS [s] -LEFT JOIN [Root_OptionalReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r5] ON [s].[RelationshipsTrunkRelationshipsRootId] = [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [s].[Id], [s].[RelationshipsRootId], [s].[RelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] +SELECT [r0].[RelatedTypeRootEntityId], [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String] +FROM [RootEntity] AS [r] +INNER JOIN [RequiredRelated_NestedCollection] AS [r0] ON [r].[Id] = [r0].[RelatedTypeRootEntityId] """); } } - public override async Task Select_subquery_root_set_trunk_FirstOrDefault_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task SelectMany_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_subquery_root_set_trunk_FirstOrDefault_collection(async, queryTrackingBehavior); + await base.SelectMany_optional_related_nested_collection(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r].[Id], [s].[Id], [s].[RelationshipsRootId], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[Id1], [s0].[Name], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchId11], [s0].[Id10], [s0].[Name0], [s0].[Name1], [s0].[Name2], [s].[c] -FROM [RelationshipsRoot] AS [r] -OUTER APPLY ( - SELECT TOP(1) 1 AS [c], [r0].[Id], [r1].[RelationshipsRootId] - FROM [RelationshipsRoot] AS [r0] - LEFT JOIN [Root_RequiredReferenceTrunk] AS [r1] ON [r0].[Id] = [r1].[RelationshipsRootId] - ORDER BY [r0].[Id] -) AS [s] -LEFT JOIN ( - SELECT [r2].[RelationshipsTrunkRelationshipsRootId], [r2].[Id1], [r2].[Name], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchId1], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r4].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r5].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r5].[Id1] AS [Id10], [r5].[Name] AS [Name0], [r3].[Name] AS [Name1], [r4].[Name] AS [Name2] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r2] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r3] ON [r2].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r2].[Id1] = [r3].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r4] ON [r2].[RelationshipsTrunkRelationshipsRootId] = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r2].[Id1] = [r4].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r5] ON [r2].[RelationshipsTrunkRelationshipsRootId] = [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r2].[Id1] = [r5].[RelationshipsBranchId1] -) AS [s0] ON [s].[RelationshipsRootId] = [s0].[RelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [s].[Id], [s].[RelationshipsRootId], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[Id1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchId11] +SELECT [o].[RelatedTypeRootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String] +FROM [RootEntity] AS [r] +INNER JOIN [OptionalRelated_NestedCollection] AS [o] ON CASE + WHEN [r].[OptionalRelated_Id] IS NOT NULL AND [r].[OptionalRelated_Int] IS NOT NULL AND [r].[OptionalRelated_Name] IS NOT NULL AND [r].[OptionalRelated_String] IS NOT NULL THEN [r].[Id] +END = [o].[RelatedTypeRootEntityId] """); } } - public override async Task Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(async, queryTrackingBehavior); + #endregion Collection - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r].[Id], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [s].[Id], [s].[RelationshipsRootId], [s].[RelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[Id1], [s0].[Name], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchId11], [s0].[Id10], [s0].[Name0], [s0].[Name1], [s0].[Name2], [s].[Name], [s1].[RelationshipsTrunkRelationshipsRootId], [s1].[Id1], [s1].[Name], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsBranchId1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s1].[RelationshipsBranchId10], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s1].[RelationshipsBranchId11], [s1].[Id10], [s1].[Name0], [s1].[Name1], [s1].[Name2], [s].[Name0], [r18].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r18].[Id1], [r18].[Name], [s].[Name1], [s].[Name2], [s].[Name3], [r19].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r19].[Id1], [r19].[Name], [s].[Name4], [s].[Name5], [r20].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r20].[Id1], [r20].[Name], [s].[Name6], [s].[c] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN [Root_RequiredReferenceTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch] AS [r1] ON [r0].[RelationshipsRootId] = [r1].[RelationshipsTrunkRelationshipsRootId] -OUTER APPLY ( - SELECT TOP(1) [r3].[RelationshipsRootId], [r3].[Name], [r4].[RelationshipsTrunkRelationshipsRootId], [r4].[Name] AS [Name0], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r5].[Name] AS [Name1], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r6].[Name] AS [Name2], [r7].[RelationshipsTrunkRelationshipsRootId] AS [RelationshipsTrunkRelationshipsRootId0], [r7].[Name] AS [Name3], [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r8].[Name] AS [Name4], [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [r9].[Name] AS [Name5], [r1].[Name] AS [Name6], 1 AS [c], [r2].[Id] - FROM [RelationshipsRoot] AS [r2] - LEFT JOIN [Root_RequiredReferenceTrunk] AS [r3] ON [r2].[Id] = [r3].[RelationshipsRootId] - LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch] AS [r4] ON [r3].[RelationshipsRootId] = [r4].[RelationshipsTrunkRelationshipsRootId] - LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r5] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] - LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r6] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] - LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch] AS [r7] ON [r3].[RelationshipsRootId] = [r7].[RelationshipsTrunkRelationshipsRootId] - LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r8] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] - LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r9] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] - ORDER BY [r2].[Id] -) AS [s] -LEFT JOIN ( - SELECT [r10].[RelationshipsTrunkRelationshipsRootId], [r10].[Id1], [r10].[Name], [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[RelationshipsBranchId1], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r12].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r13].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r13].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r13].[Id1] AS [Id10], [r13].[Name] AS [Name0], [r11].[Name] AS [Name1], [r12].[Name] AS [Name2] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r10] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r11] ON [r10].[RelationshipsTrunkRelationshipsRootId] = [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r10].[Id1] = [r11].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r12] ON [r10].[RelationshipsTrunkRelationshipsRootId] = [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r10].[Id1] = [r12].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r13] ON [r10].[RelationshipsTrunkRelationshipsRootId] = [r13].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r10].[Id1] = [r13].[RelationshipsBranchId1] -) AS [s0] ON [r0].[RelationshipsRootId] = [s0].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN ( - SELECT [r14].[RelationshipsTrunkRelationshipsRootId], [r14].[Id1], [r14].[Name], [r15].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r15].[RelationshipsBranchId1], [r16].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r16].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r17].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r17].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r17].[Id1] AS [Id10], [r17].[Name] AS [Name0], [r15].[Name] AS [Name1], [r16].[Name] AS [Name2] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r14] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r15] ON [r14].[RelationshipsTrunkRelationshipsRootId] = [r15].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r14].[Id1] = [r15].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r16] ON [r14].[RelationshipsTrunkRelationshipsRootId] = [r16].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r14].[Id1] = [r16].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r17] ON [r14].[RelationshipsTrunkRelationshipsRootId] = [r17].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r14].[Id1] = [r17].[RelationshipsBranchId1] -) AS [s1] ON [s].[RelationshipsRootId] = [s1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r18] ON [s].[RelationshipsTrunkRelationshipsRootId] = [r18].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r19] ON [s].[RelationshipsTrunkRelationshipsRootId0] = [r19].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r20] ON [s].[RelationshipsTrunkRelationshipsRootId0] = [r20].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [s].[Id], [s].[RelationshipsRootId], [s].[RelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId2], [s0].[RelationshipsTrunkRelationshipsRootId], [s0].[Id1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s0].[RelationshipsBranchId1], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s0].[RelationshipsBranchId10], [s0].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s0].[RelationshipsBranchId11], [s0].[Id10], [s1].[RelationshipsTrunkRelationshipsRootId], [s1].[Id1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s1].[RelationshipsBranchId1], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s1].[RelationshipsBranchId10], [s1].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s1].[RelationshipsBranchId11], [s1].[Id10], [r18].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r18].[Id1], [r19].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r19].[Id1], [r20].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] -"""); - } - } + #region Multiple - public override async Task Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_root_duplicated(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(async, queryTrackingBehavior); + await base.Select_root_duplicated(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r].[Id], [r0].[RelationshipsRootId], [r6].[Id], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchId11], [s].[Id10], [s].[Name0], [s].[Name1], [s].[Name2], [r6].[c] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN [Root_RequiredReferenceTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -OUTER APPLY ( - SELECT TOP(1) 1 AS [c], [r1].[Id] - FROM [RelationshipsRoot] AS [r1] - ORDER BY [r1].[Id] -) AS [r6] -LEFT JOIN ( - SELECT [r2].[RelationshipsTrunkRelationshipsRootId], [r2].[Id1], [r2].[Name], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchId1], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r4].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r5].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r5].[Id1] AS [Id10], [r5].[Name] AS [Name0], [r3].[Name] AS [Name1], [r4].[Name] AS [Name2] - FROM [Root_RequiredReferenceTrunk_CollectionBranch] AS [r2] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r3] ON [r2].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r2].[Id1] = [r3].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r4] ON [r2].[RelationshipsTrunkRelationshipsRootId] = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r2].[Id1] = [r4].[RelationshipsBranchId1] - LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r5] ON [r2].[RelationshipsTrunkRelationshipsRootId] = [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r2].[Id1] = [r5].[RelationshipsBranchId1] -) AS [s] ON [r0].[RelationshipsRootId] = [s].[RelationshipsTrunkRelationshipsRootId] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [r6].[Id], [s].[RelationshipsTrunkRelationshipsRootId], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchId11] + AssertSql( + """ +SELECT [r].[Id], [r].[Name], [r].[OptionalRelated_Id], [r].[OptionalRelated_Int], [r].[OptionalRelated_Name], [r].[OptionalRelated_String], [o].[RelatedTypeRootEntityId], [o].[Id], [o].[Int], [o].[Name], [o].[String], [r].[OptionalRelated_OptionalNested_Id], [r].[OptionalRelated_OptionalNested_Int], [r].[OptionalRelated_OptionalNested_Name], [r].[OptionalRelated_OptionalNested_String], [r].[OptionalRelated_RequiredNested_Id], [r].[OptionalRelated_RequiredNested_Int], [r].[OptionalRelated_RequiredNested_Name], [r].[OptionalRelated_RequiredNested_String], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Name], [s].[String], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[Id0], [s].[Int0], [s].[Name0], [s].[String0], [s].[OptionalNested_Id], [s].[OptionalNested_Int], [s].[OptionalNested_Name], [s].[OptionalNested_String], [s].[RequiredNested_Id], [s].[RequiredNested_Int], [s].[RequiredNested_Name], [s].[RequiredNested_String], [r].[RequiredRelated_Id], [r].[RequiredRelated_Int], [r].[RequiredRelated_Name], [r].[RequiredRelated_String], [r2].[RelatedTypeRootEntityId], [r2].[Id], [r2].[Int], [r2].[Name], [r2].[String], [r].[RequiredRelated_OptionalNested_Id], [r].[RequiredRelated_OptionalNested_Int], [r].[RequiredRelated_OptionalNested_Name], [r].[RequiredRelated_OptionalNested_String], [r].[RequiredRelated_RequiredNested_Id], [r].[RequiredRelated_RequiredNested_Int], [r].[RequiredRelated_RequiredNested_Name], [r].[RequiredRelated_RequiredNested_String], [o0].[RelatedTypeRootEntityId], [o0].[Id], [o0].[Int], [o0].[Name], [o0].[String], [s0].[RootEntityId], [s0].[Id], [s0].[Int], [s0].[Name], [s0].[String], [s0].[RelatedTypeRootEntityId], [s0].[RelatedTypeId], [s0].[Id0], [s0].[Int0], [s0].[Name0], [s0].[String0], [s0].[OptionalNested_Id], [s0].[OptionalNested_Int], [s0].[OptionalNested_Name], [s0].[OptionalNested_String], [s0].[RequiredNested_Id], [s0].[RequiredNested_Int], [s0].[RequiredNested_Name], [s0].[RequiredNested_String], [r5].[RelatedTypeRootEntityId], [r5].[Id], [r5].[Int], [r5].[Name], [r5].[String] +FROM [RootEntity] AS [r] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o] ON CASE + WHEN [r].[OptionalRelated_Id] IS NOT NULL AND [r].[OptionalRelated_Int] IS NOT NULL AND [r].[OptionalRelated_Name] IS NOT NULL AND [r].[OptionalRelated_String] IS NOT NULL THEN [r].[Id] +END = [o].[RelatedTypeRootEntityId] +LEFT JOIN ( + SELECT [r0].[RootEntityId], [r0].[Id], [r0].[Int], [r0].[Name], [r0].[String], [r1].[RelatedTypeRootEntityId], [r1].[RelatedTypeId], [r1].[Id] AS [Id0], [r1].[Int] AS [Int0], [r1].[Name] AS [Name0], [r1].[String] AS [String0], [r0].[OptionalNested_Id], [r0].[OptionalNested_Int], [r0].[OptionalNested_Name], [r0].[OptionalNested_String], [r0].[RequiredNested_Id], [r0].[RequiredNested_Int], [r0].[RequiredNested_Name], [r0].[RequiredNested_String] + FROM [RelatedCollection] AS [r0] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r1] ON [r0].[RootEntityId] = [r1].[RelatedTypeRootEntityId] AND [r0].[Id] = [r1].[RelatedTypeId] +) AS [s] ON [r].[Id] = [s].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r2] ON [r].[Id] = [r2].[RelatedTypeRootEntityId] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o0] ON CASE + WHEN [r].[OptionalRelated_Id] IS NOT NULL AND [r].[OptionalRelated_Int] IS NOT NULL AND [r].[OptionalRelated_Name] IS NOT NULL AND [r].[OptionalRelated_String] IS NOT NULL THEN [r].[Id] +END = [o0].[RelatedTypeRootEntityId] +LEFT JOIN ( + SELECT [r3].[RootEntityId], [r3].[Id], [r3].[Int], [r3].[Name], [r3].[String], [r4].[RelatedTypeRootEntityId], [r4].[RelatedTypeId], [r4].[Id] AS [Id0], [r4].[Int] AS [Int0], [r4].[Name] AS [Name0], [r4].[String] AS [String0], [r3].[OptionalNested_Id], [r3].[OptionalNested_Int], [r3].[OptionalNested_Name], [r3].[OptionalNested_String], [r3].[RequiredNested_Id], [r3].[RequiredNested_Int], [r3].[RequiredNested_Name], [r3].[RequiredNested_String] + FROM [RelatedCollection] AS [r3] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r4] ON [r3].[RootEntityId] = [r4].[RelatedTypeRootEntityId] AND [r3].[Id] = [r4].[RelatedTypeId] +) AS [s0] ON [r].[Id] = [s0].[RootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r5] ON [r].[Id] = [r5].[RelatedTypeRootEntityId] +ORDER BY [r].[Id], [o].[RelatedTypeRootEntityId], [o].[Id], [s].[RootEntityId], [s].[Id], [s].[RelatedTypeRootEntityId], [s].[RelatedTypeId], [s].[Id0], [r2].[RelatedTypeRootEntityId], [r2].[Id], [o0].[RelatedTypeRootEntityId], [o0].[Id], [s0].[RootEntityId], [s0].[Id], [s0].[RelatedTypeRootEntityId], [s0].[RelatedTypeId], [s0].[Id0], [r5].[RelatedTypeRootEntityId] """); - } } - #endregion Subquery - - #region SelectMany - - public override async Task SelectMany_trunk_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - { - await base.SelectMany_trunk_collection(async, queryTrackingBehavior); + #endregion Multiple - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else - { - AssertSql( - """ -SELECT [r0].[RelationshipsRootId], [r0].[Id1], [r0].[Name], [r].[Id], [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[RelationshipsTrunkId1], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkId1], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkId1], [r4].[RelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsTrunkId1], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsBranchRelationshipsTrunkId1], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchRelationshipsTrunkId1], [s].[RelationshipsTrunkRelationshipsRootId], [s].[RelationshipsTrunkId1], [s].[Id1], [s].[Name], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchRelationshipsTrunkId1], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchRelationshipsTrunkId10], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchRelationshipsTrunkId11], [s].[RelationshipsBranchId11], [s].[Id10], [s].[Name0], [s].[Name1], [s].[Name2], [r1].[Name], [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[RelationshipsBranchRelationshipsTrunkId1], [r11].[Id1], [r11].[Name], [r2].[Name], [r3].[Name], [r4].[Name], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r12].[RelationshipsBranchRelationshipsTrunkId1], [r12].[Id1], [r12].[Name], [r5].[Name], [r6].[Name] -FROM [RelationshipsRoot] AS [r] -INNER JOIN [RelationshipsRoot_CollectionTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch] AS [r1] ON [r0].[RelationshipsRootId] = [r1].[RelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [r1].[RelationshipsTrunkId1] -LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_OptionalReferenceLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[RelationshipsTrunkId1] = [r2].[RelationshipsBranchRelationshipsTrunkId1] -LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_RequiredReferenceLeaf] AS [r3] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[RelationshipsTrunkId1] = [r3].[RelationshipsBranchRelationshipsTrunkId1] -LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch] AS [r4] ON [r0].[RelationshipsRootId] = [r4].[RelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [r4].[RelationshipsTrunkId1] -LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_OptionalReferenceLeaf] AS [r5] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r4].[RelationshipsTrunkId1] = [r5].[RelationshipsBranchRelationshipsTrunkId1] -LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_RequiredReferenceLeaf] AS [r6] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r4].[RelationshipsTrunkId1] = [r6].[RelationshipsBranchRelationshipsTrunkId1] -LEFT JOIN ( - SELECT [r7].[RelationshipsTrunkRelationshipsRootId], [r7].[RelationshipsTrunkId1], [r7].[Id1], [r7].[Name], [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r8].[RelationshipsBranchRelationshipsTrunkId1], [r8].[RelationshipsBranchId1], [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [r9].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId10], [r9].[RelationshipsBranchId1] AS [RelationshipsBranchId10], [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AS [RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [r10].[RelationshipsBranchRelationshipsTrunkId1] AS [RelationshipsBranchRelationshipsTrunkId11], [r10].[RelationshipsBranchId1] AS [RelationshipsBranchId11], [r10].[Id1] AS [Id10], [r10].[Name] AS [Name0], [r8].[Name] AS [Name1], [r9].[Name] AS [Name2] - FROM [RelationshipsRoot_CollectionTrunk_CollectionBranch] AS [r7] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r8] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r8].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r7].[RelationshipsTrunkId1] = [r8].[RelationshipsBranchRelationshipsTrunkId1] AND [r7].[Id1] = [r8].[RelationshipsBranchId1] - LEFT JOIN [Root_CollectionTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r9] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r9].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r7].[RelationshipsTrunkId1] = [r9].[RelationshipsBranchRelationshipsTrunkId1] AND [r7].[Id1] = [r9].[RelationshipsBranchId1] - LEFT JOIN [RelationshipsRoot_CollectionTrunk_CollectionBranch_CollectionLeaf] AS [r10] ON [r7].[RelationshipsTrunkRelationshipsRootId] = [r10].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r7].[RelationshipsTrunkId1] = [r10].[RelationshipsBranchRelationshipsTrunkId1] AND [r7].[Id1] = [r10].[RelationshipsBranchId1] -) AS [s] ON [r0].[RelationshipsRootId] = [s].[RelationshipsTrunkRelationshipsRootId] AND [r0].[Id1] = [s].[RelationshipsTrunkId1] -LEFT JOIN [Root_CollectionTrunk_OptionalReferenceBranch_CollectionLeaf] AS [r11] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[RelationshipsTrunkId1] = [r11].[RelationshipsBranchRelationshipsTrunkId1] -LEFT JOIN [Root_CollectionTrunk_RequiredReferenceBranch_CollectionLeaf] AS [r12] ON [r4].[RelationshipsTrunkRelationshipsRootId] = [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r4].[RelationshipsTrunkId1] = [r12].[RelationshipsBranchRelationshipsTrunkId1] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [r0].[Id1], [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[RelationshipsTrunkId1], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkId1], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchRelationshipsTrunkId1], [r4].[RelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsTrunkId1], [r5].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r5].[RelationshipsBranchRelationshipsTrunkId1], [r6].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r6].[RelationshipsBranchRelationshipsTrunkId1], [s].[RelationshipsTrunkRelationshipsRootId], [s].[RelationshipsTrunkId1], [s].[Id1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [s].[RelationshipsBranchRelationshipsTrunkId1], [s].[RelationshipsBranchId1], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId0], [s].[RelationshipsBranchRelationshipsTrunkId10], [s].[RelationshipsBranchId10], [s].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId1], [s].[RelationshipsBranchRelationshipsTrunkId11], [s].[RelationshipsBranchId11], [s].[Id10], [r11].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r11].[RelationshipsBranchRelationshipsTrunkId1], [r11].[Id1], [r12].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r12].[RelationshipsBranchRelationshipsTrunkId1] -"""); - } - } + #region Subquery - public override async Task SelectMany_required_trunk_reference_branch_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_subquery_required_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.SelectMany_required_trunk_reference_branch_collection(async, queryTrackingBehavior); + await base.Select_subquery_required_related_FirstOrDefault(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[Id1], [r1].[Name], [r].[Id], [r0].[RelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchId1], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchId1], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsBranchId1], [r4].[Id1], [r4].[Name], [r2].[Name], [r3].[Name] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN [Root_RequiredReferenceTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -INNER JOIN [Root_RequiredReferenceTrunk_CollectionBranch] AS [r1] ON [r0].[RelationshipsRootId] = [r1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[Id1] = [r2].[RelationshipsBranchId1] -LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r3] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[Id1] = [r3].[RelationshipsBranchId1] -LEFT JOIN [Root_RequiredReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r4] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[Id1] = [r4].[RelationshipsBranchId1] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[Id1], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchId1], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchId1], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsBranchId1] +SELECT [r1].[Id], [r1].[RequiredRelated_RequiredNested_Id], [r1].[RequiredRelated_RequiredNested_Int], [r1].[RequiredRelated_RequiredNested_Name], [r1].[RequiredRelated_RequiredNested_String] +FROM [RootEntity] AS [r] +OUTER APPLY ( + SELECT TOP(1) [r0].[Id], [r0].[RequiredRelated_RequiredNested_Id], [r0].[RequiredRelated_RequiredNested_Int], [r0].[RequiredRelated_RequiredNested_Name], [r0].[RequiredRelated_RequiredNested_String] + FROM [RootEntity] AS [r0] + ORDER BY [r0].[Id] +) AS [r1] """); } } - public override async Task SelectMany_optional_trunk_reference_branch_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + public override async Task Select_subquery_optional_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) { - await base.SelectMany_optional_trunk_reference_branch_collection(async, queryTrackingBehavior); + await base.Select_subquery_optional_related_FirstOrDefault(async, queryTrackingBehavior); - if (queryTrackingBehavior is QueryTrackingBehavior.TrackAll) - { - AssertSql(); - } - else + if (queryTrackingBehavior is not QueryTrackingBehavior.TrackAll) { AssertSql( """ -SELECT [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[Id1], [r1].[Name], [r].[Id], [r0].[RelationshipsRootId], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchId1], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchId1], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsBranchId1], [r4].[Id1], [r4].[Name], [r2].[Name], [r3].[Name] -FROM [RelationshipsRoot] AS [r] -LEFT JOIN [Root_OptionalReferenceTrunk] AS [r0] ON [r].[Id] = [r0].[RelationshipsRootId] -INNER JOIN [Root_OptionalReferenceTrunk_CollectionBranch] AS [r1] ON [r0].[RelationshipsRootId] = [r1].[RelationshipsTrunkRelationshipsRootId] -LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_OptionalReferenceLeaf] AS [r2] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[Id1] = [r2].[RelationshipsBranchId1] -LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_RequiredReferenceLeaf] AS [r3] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[Id1] = [r3].[RelationshipsBranchId1] -LEFT JOIN [Root_OptionalReferenceTrunk_CollectionBranch_CollectionLeaf] AS [r4] ON [r1].[RelationshipsTrunkRelationshipsRootId] = [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId] AND [r1].[Id1] = [r4].[RelationshipsBranchId1] -ORDER BY [r].[Id], [r0].[RelationshipsRootId], [r1].[RelationshipsTrunkRelationshipsRootId], [r1].[Id1], [r2].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r2].[RelationshipsBranchId1], [r3].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r3].[RelationshipsBranchId1], [r4].[RelationshipsBranchRelationshipsTrunkRelationshipsRootId], [r4].[RelationshipsBranchId1] +SELECT [r1].[Id], [r1].[OptionalRelated_RequiredNested_Id], [r1].[OptionalRelated_RequiredNested_Int], [r1].[OptionalRelated_RequiredNested_Name], [r1].[OptionalRelated_RequiredNested_String] +FROM [RootEntity] AS [r] +OUTER APPLY ( + SELECT TOP(1) [r0].[Id], [r0].[OptionalRelated_RequiredNested_Id], [r0].[OptionalRelated_RequiredNested_Int], [r0].[OptionalRelated_RequiredNested_Name], [r0].[OptionalRelated_RequiredNested_String] + FROM [RootEntity] AS [r0] + ORDER BY [r0].[Id] +) AS [r1] """); } } - #endregion SelectMany + #endregion Subquery [ConditionalFact] public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); - - private void AssertSql(params string[] expected) - => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingRelationshipsSqlServerFixture.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingSqlServerFixture.cs similarity index 58% rename from test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingRelationshipsSqlServerFixture.cs rename to test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingSqlServerFixture.cs index c47c2aef8ff..942f32d0fef 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingRelationshipsSqlServerFixture.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingSqlServerFixture.cs @@ -3,11 +3,8 @@ namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedTableSplitting; -public class OwnedTableSplittingRelationshipsSqlServerFixture : OwnedTableSplittingRelationalFixtureBase, ITestSqlLoggerFactory +public class OwnedTableSplittingSqlServerFixture : OwnedTableSplittingRelationalFixtureBase { protected override ITestStoreFactory TestStoreFactory => SqlServerTestStoreFactory.Instance; - - public TestSqlLoggerFactory TestSqlLoggerFactory - => (TestSqlLoggerFactory)ListLoggerFactory; } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs index 7097461ff80..49a75281aa6 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs @@ -17,29 +17,29 @@ public ComplexTypeQuerySqliteTest(ComplexTypeQuerySqliteFixture fixture, ITestOu Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } - public override async Task Filter_on_property_inside_complex_type(bool async) - { - await base.Filter_on_property_inside_complex_type(async); - - AssertSql( - """ -SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName" -FROM "Customer" AS "c" -WHERE "c"."ShippingAddress_ZipCode" = 7728 -"""); - } - - public override async Task Filter_on_property_inside_nested_complex_type(bool async) - { - await base.Filter_on_property_inside_nested_complex_type(async); - - AssertSql( - """ -SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName" -FROM "Customer" AS "c" -WHERE "c"."ShippingAddress_Country_Code" = 'DE' -"""); - } +// public override async Task Filter_on_property_inside_complex_type(bool async) +// { +// await base.Filter_on_property_inside_complex_type(async); + +// AssertSql( +// """ +// SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName" +// FROM "Customer" AS "c" +// WHERE "c"."ShippingAddress_ZipCode" = 7728 +// """); +// } + +// public override async Task Filter_on_property_inside_nested_complex_type(bool async) +// { +// await base.Filter_on_property_inside_nested_complex_type(async); + +// AssertSql( +// """ +// SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName" +// FROM "Customer" AS "c" +// WHERE "c"."ShippingAddress_Country_Code" = 'DE' +// """); +// } public override async Task Filter_on_property_inside_complex_type_after_subquery(bool async) { diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonCollectionSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonCollectionSqliteTest.cs new file mode 100644 index 00000000000..d5e4bdf3180 --- /dev/null +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonCollectionSqliteTest.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Data.Sqlite; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexJson; + +public class ComplexJsonCollectionSqliteTest(ComplexJsonSqliteFixture fixture, ITestOutputHelper testOutputHelper) + : ComplexJsonCollectionRelationalTestBase(fixture, testOutputHelper) +{ + // TODO: #36296 + public override Task Index_column(bool async) + => Assert.ThrowsAsync(() => base.Index_column(async)); +} diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonMiscellaneousSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonMiscellaneousSqliteTest.cs new file mode 100644 index 00000000000..710494a693b --- /dev/null +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonMiscellaneousSqliteTest.cs @@ -0,0 +1,9 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexJson; + +public class ComplexJsonMiscellaneousSqliteTest(ComplexJsonSqliteFixture fixture, ITestOutputHelper testOutputHelper) + : ComplexJsonMiscellaneousRelationalTestBase(fixture, testOutputHelper) +{ +} diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonProjectionSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonProjectionSqliteTest.cs new file mode 100644 index 00000000000..59c16b94cbb --- /dev/null +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonProjectionSqliteTest.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.EntityFrameworkCore.Sqlite.Internal; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexJson; + +public class ComplexJsonProjectionSqliteTest(ComplexJsonSqliteFixture fixture, ITestOutputHelper testOutputHelper) + : ComplexJsonProjectionRelationalTestBase(fixture, testOutputHelper) +{ + public override Task SelectMany_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertApplyNotSupported(() => base.SelectMany_related_collection(async, queryTrackingBehavior)); + + public override Task SelectMany_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertApplyNotSupported(() => base.SelectMany_required_related_nested_collection(async, queryTrackingBehavior)); + + public override Task SelectMany_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertApplyNotSupported(() => base.SelectMany_optional_related_nested_collection(async, queryTrackingBehavior)); + + public override Task Select_subquery_required_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertApplyNotSupported(() => base.Select_subquery_required_related_FirstOrDefault(async, queryTrackingBehavior)); + + public override Task Select_subquery_optional_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertApplyNotSupported(() => base.Select_subquery_optional_related_FirstOrDefault(async, queryTrackingBehavior)); + + private static async Task AssertApplyNotSupported(Func query) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync(query)) + .Message); +} diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonSqliteFixture.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonSqliteFixture.cs new file mode 100644 index 00000000000..da9b8bcac5d --- /dev/null +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexJson/ComplexJsonSqliteFixture.cs @@ -0,0 +1,105 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexJson; + +public class ComplexJsonSqliteFixture : ComplexJsonRelationalFixtureBase +{ + protected override ITestStoreFactory TestStoreFactory + => SqliteTestStoreFactory.Instance; + + protected override async Task SeedAsync(PoolableDbContext context) + { + // TODO: Temporary, until we have update pipeline support for complex JSON + await context.Database.ExecuteSqlAsync($$$""" +INSERT INTO RootEntity (Id, Name, RequiredRelated, OptionalRelated, RelatedCollection) VALUES +( + 1, + 'Root1', + -- RequiredRelated: + '{ + "Id": 100, + "Name": "Root1_RequiredRelated", + "Int": 8, + "String": "foo", + "RequiredNested": { "Id": 1000, "Name": "Root1_RequiredRelated_RequiredNested", "Int": 50, "String": "foo_foo" }, + "OptionalNested": { "Id": 1001, "Name": "Root1_RequiredRelated_OptionalNested", "Int": 51, "String": "foo_bar" }, + "NestedCollection": + [ + { "Id": 1002, "Name": "Root1_RequiredRelated_NestedCollection_1", "Int": 52, "String": "foo_baz1" }, + { "Id": 1003, "Name": "Root1_RequiredRelated_NestedCollection_2", "Int": 53, "String": "foo_baz2" } + ] + }', + -- OptionalRelated: + '{ + "Id": 101, + "Name": "Root1_OptionalRelated", + "Int": 9, + "String": "bar", + "RequiredNested": { "Id": 1010, "Name": "Root1_OptionalRelated_RequiredNested", "Int": 52, "String": "bar_foo" }, + "OptionalNested": { "Id": 1011, "Name": "Root1_OptionalRelated_OptionalNested", "Int": 53, "String": "bar_bar" }, + "NestedCollection": + [ + { "Id": 1012, "Name": "Root1_OptionalRelated_NestedCollection_1", "Int": 54, "String": "bar_baz1" }, + { "Id": 1013, "Name": "Root1_OptionalRelated_NestedCollection_2", "Int": 55, "String": "bar_baz2" } + ] + }', + -- RelatedCollection: + '[ + { + "Id": 102, + "Name": "Root1_RelatedCollection_1", + "Int": 21, + "String": "foo", + "RequiredNested": { "Id": 1020, "Name": "Root1_RelatedCollection_1_RequiredNested", "Int": 50, "String": "foo_foo" }, + "OptionalNested": { "Id": 1021, "Name": "Root1_RelatedCollection_1_OptionalNested", "Int": 51, "String": "foo_bar" }, + "NestedCollection": + [ + { "Id": 1022, "Name": "Root1_RelatedCollection_1_NestedCollection_1", "Int": 53, "String": "foo_bar" }, + { "Id": 1023, "Name": "Root1_RelatedCollection_1_NestedCollection_2", "Int": 51, "String": "foo_bar" } + ] + }, + { + "Id": 103, + "Name": "Root1_RelatedCollection_2", + "Int": 22, + "String": "foo", + "RequiredNested": { "Id": 1030, "Name": "Root1_RelatedCollection_2_RequiredNested", "Int": 50, "String": "foo_foo" }, + "OptionalNested": { "Id": 1031, "Name": "Root1_RelatedCollection_2_OptionalNested", "Int": 51, "String": "foo_bar" }, + "NestedCollection": + [ + { "Id": 1032, "Name": "Root1_RelatedCollection_2_NestedCollection_1", "Int": 53, "String": "foo_bar" }, + { "Id": 1033, "Name": "Root1_RelatedCollection_2_NestedCollection_2", "Int": 51, "String": "foo_bar" } + ] + } + ]' +), +( + 2, + 'Root2', + -- RequiredRelated: + '{ + "Id": 200, + "Name": "Root2_RequiredRelated", + "Int": 10, + "String": "aaa", + "RequiredNested": { "Id": 2000, "Name": "Root2_RequiredRelated_RequiredNested", "Int": 54, "String": "aaa_xxx" }, + "OptionalNested": { "Id": 2001, "Name": "Root2_RequiredRelated_OptionalNested", "Int": 55, "String": "aaa_yyy" }, + "NestedCollection": [] + }', + -- OptionalRelated: + '{ + "Id": 201, + "Name": "Root2_OptionalRelated", + "Int": 11, + "String": "bbb", + "RequiredNested": { "Id": 2010, "Name": "Root2_OptionalRelated_RequiredNested", "Int": 56, "String": "bbb_xxx" }, + "OptionalNested": { "Id": 2011, "Name": "Root2_OptionalRelated_OptionalNested", "Int": 57, "String": "bbb_yyy" }, + "NestedCollection": [] + }', + -- RelatedCollection: + '[]' +) +"""); + } +} diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingMiscellaneousSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingMiscellaneousSqliteTest.cs new file mode 100644 index 00000000000..6c052704357 --- /dev/null +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingMiscellaneousSqliteTest.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexTableSplitting; + +public class ComplexTableSplittingMiscellaneousSqliteTest( + ComplexTableSplittingSqliteFixture fixture, + ITestOutputHelper testOutputHelper) + : ComplexTableSplittingMiscellaneousRelationalTestBase(fixture, testOutputHelper) +{ +} diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingProjectionSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingProjectionSqliteTest.cs index 1ad4ffce479..ab895ffa62a 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingProjectionSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingProjectionSqliteTest.cs @@ -1,15 +1,19 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.EntityFrameworkCore.Sqlite.Internal; + namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexTableSplitting; -public class ComplexTableSplittingProjectionSqliteTest - : ComplexTableSplittingProjectionRelationalTestBase +public class ComplexTableSplittingProjectionSqliteTest(ComplexTableSplittingSqliteFixture fixture, ITestOutputHelper testOutputHelper) + : ComplexTableSplittingProjectionRelationalTestBase(fixture, testOutputHelper) { - public ComplexTableSplittingProjectionSqliteTest(ComplexTableSplittingSqliteFixture fixture, ITestOutputHelper testOutputHelper) - : base(fixture) - { - Fixture.TestSqlLoggerFactory.Clear(); - Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); - } + public override Task Select_subquery_optional_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertApplyNotSupported(() => base.Select_subquery_optional_related_FirstOrDefault(async, queryTrackingBehavior)); + + private static async Task AssertApplyNotSupported(Func query) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync(query)) + .Message); } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingSqliteFixture.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingSqliteFixture.cs index 5a21d0cb802..8f261d59c2d 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingSqliteFixture.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/ComplexTableSplitting/ComplexTableSplittingSqliteFixture.cs @@ -3,11 +3,8 @@ namespace Microsoft.EntityFrameworkCore.Query.Relationships.ComplexTableSplitting; -public class ComplexTableSplittingSqliteFixture : ComplexTableSplittingRelationalFixtureBase, ITestSqlLoggerFactory +public class ComplexTableSplittingSqliteFixture : ComplexTableSplittingRelationalFixtureBase { protected override ITestStoreFactory TestStoreFactory => SqliteTestStoreFactory.Instance; - - public TestSqlLoggerFactory TestSqlLoggerFactory - => (TestSqlLoggerFactory)ListLoggerFactory; } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/JsonOwnedNavigations/JsonOwnedNavigationsProjectionSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/JsonOwnedNavigations/JsonOwnedNavigationsProjectionSqliteTest.cs deleted file mode 100644 index 318467b7062..00000000000 --- a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/JsonOwnedNavigations/JsonOwnedNavigationsProjectionSqliteTest.cs +++ /dev/null @@ -1,5 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -// All tests in OwnedNavigationsProjectionSqliteTest currently fail because of #26708 -// (Stop generating composite keys for owned collections on SQLite) diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/JsonOwnedNavigations/JsonOwnedNavigationsSqliteFixture.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/JsonOwnedNavigations/JsonOwnedNavigationsSqliteFixture.cs deleted file mode 100644 index 7d1840193fe..00000000000 --- a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/JsonOwnedNavigations/JsonOwnedNavigationsSqliteFixture.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.EntityFrameworkCore.Query.Relationships.JsonOwnedNavigations; - -public class JsonOwnedNavigationsSqliteFixture : JsonOwnedNavigationsRelationalFixtureBase, ITestSqlLoggerFactory -{ - protected override ITestStoreFactory TestStoreFactory - => SqliteTestStoreFactory.Instance; - - public TestSqlLoggerFactory TestSqlLoggerFactory - => (TestSqlLoggerFactory)ListLoggerFactory; -} diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/Navigations/NavigationsCollectionSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/Navigations/NavigationsCollectionSqliteTest.cs new file mode 100644 index 00000000000..eadf201ae8c --- /dev/null +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/Navigations/NavigationsCollectionSqliteTest.cs @@ -0,0 +1,9 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; + +public class NavigationsCollectionSqliteTest(NavigationsSqliteFixture fixture, ITestOutputHelper testOutputHelper) + : NavigationsCollectionRelationalTestBase(fixture, testOutputHelper) +{ +} diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/Navigations/NavigationsIncludeSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/Navigations/NavigationsIncludeSqliteTest.cs index 3d27bd0bf53..4877974d684 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/Navigations/NavigationsIncludeSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/Navigations/NavigationsIncludeSqliteTest.cs @@ -3,13 +3,7 @@ namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; -public class NavigationsIncludeSqliteTest - : NavigationsIncludeRelationalTestBase +public class NavigationsIncludeSqliteTest(NavigationsSqliteFixture fixture, ITestOutputHelper testOutputHelper) + : NavigationsIncludeRelationalTestBase(fixture, testOutputHelper) { - public NavigationsIncludeSqliteTest(NavigationsSqliteFixture fixture, ITestOutputHelper testOutputHelper) - : base(fixture) - { - Fixture.TestSqlLoggerFactory.Clear(); - Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); - } } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/Navigations/NavigationsMiscellaneousSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/Navigations/NavigationsMiscellaneousSqliteTest.cs new file mode 100644 index 00000000000..bd5d29c2fc4 --- /dev/null +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/Navigations/NavigationsMiscellaneousSqliteTest.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; + +public class NavigationsMiscellaneousSqliteTest( + NavigationsSqliteFixture fixture, + ITestOutputHelper testOutputHelper) + : NavigationsMiscellaneousRelationalTestBase(fixture, testOutputHelper) +{ +} diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/Navigations/NavigationsProjectionSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/Navigations/NavigationsProjectionSqliteTest.cs index 3a32e321e31..ff26604c200 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/Navigations/NavigationsProjectionSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/Navigations/NavigationsProjectionSqliteTest.cs @@ -5,56 +5,18 @@ namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; -public class NavigationsProjectionSqliteTest - : NavigationsProjectionRelationalTestBase +public class NavigationsProjectionSqliteTest(NavigationsSqliteFixture fixture, ITestOutputHelper testOutputHelper) + : NavigationsProjectionRelationalTestBase(fixture, testOutputHelper) { - public NavigationsProjectionSqliteTest(NavigationsSqliteFixture fixture, ITestOutputHelper testOutputHelper) - : base(fixture) - { - Fixture.TestSqlLoggerFactory.Clear(); - Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); - } + public override Task Select_subquery_required_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertApplyNotSupported(() => base.Select_subquery_required_related_FirstOrDefault(async, queryTrackingBehavior)); - #region Subquery + public override Task Select_subquery_optional_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + => AssertApplyNotSupported(() => base.Select_subquery_optional_related_FirstOrDefault(async, queryTrackingBehavior)); - public override async Task Select_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) + private static async Task AssertApplyNotSupported(Func query) => Assert.Equal( SqliteStrings.ApplyNotSupported, - (await Assert.ThrowsAsync( - () => base.Select_subquery_root_set_required_trunk_FirstOrDefault_branch(async, queryTrackingBehavior))) + (await Assert.ThrowsAsync(query)) .Message); - - public override async Task Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async, QueryTrackingBehavior queryTrackingBehavior) - => Assert.Equal( - SqliteStrings.ApplyNotSupported, - (await Assert.ThrowsAsync( - () => base.Select_subquery_root_set_optional_trunk_FirstOrDefault_branch(async, queryTrackingBehavior))) - .Message); - - public override async Task Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer( - bool async, - QueryTrackingBehavior queryTrackingBehavior) - => Assert.Equal( - SqliteStrings.ApplyNotSupported, - (await Assert.ThrowsAsync( - () => base.Select_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(async, queryTrackingBehavior))) - .Message); - - public override async Task Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault( - bool async, - QueryTrackingBehavior queryTrackingBehavior) - => Assert.Equal( - SqliteStrings.ApplyNotSupported, - (await Assert.ThrowsAsync( - () => base.Select_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(async, queryTrackingBehavior))) - .Message); - - public override async Task Select_subquery_root_set_trunk_FirstOrDefault_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) - => Assert.Equal( - SqliteStrings.ApplyNotSupported, - (await Assert.ThrowsAsync( - () => base.Select_subquery_root_set_trunk_FirstOrDefault_collection(async, queryTrackingBehavior))) - .Message); - - #endregion Subquery } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/Navigations/NavigationsSqliteFixture.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/Navigations/NavigationsSqliteFixture.cs index d93cd0cfc21..5c5103f2498 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/Navigations/NavigationsSqliteFixture.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/Navigations/NavigationsSqliteFixture.cs @@ -3,11 +3,8 @@ namespace Microsoft.EntityFrameworkCore.Query.Relationships.Navigations; -public class NavigationsSqliteFixture : NavigationsRelationalFixtureBase, ITestSqlLoggerFactory +public class NavigationsSqliteFixture : NavigationsRelationalFixtureBase { protected override ITestStoreFactory TestStoreFactory => SqliteTestStoreFactory.Instance; - - public TestSqlLoggerFactory TestSqlLoggerFactory - => (TestSqlLoggerFactory)ListLoggerFactory; } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonCollectionSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonCollectionSqliteTest.cs new file mode 100644 index 00000000000..bc152246735 --- /dev/null +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonCollectionSqliteTest.cs @@ -0,0 +1,9 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedJson; + +public class OwnedJsonCollectionSqliteTest(OwnedJsonSqliteFixture fixture, ITestOutputHelper testOutputHelper) + : OwnedJsonCollectionRelationalTestBase(fixture, testOutputHelper) +{ +} diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonMiscellaneousSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonMiscellaneousSqliteTest.cs new file mode 100644 index 00000000000..bd8d50d3e40 --- /dev/null +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonMiscellaneousSqliteTest.cs @@ -0,0 +1,9 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedJson; + +public class OwnedJsonMiscellaneousSqliteTest(OwnedJsonSqliteFixture fixture, ITestOutputHelper testOutputHelper) + : OwnedJsonMiscellaneousRelationalTestBase(fixture, testOutputHelper) +{ +} diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonProjectionSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonProjectionSqliteTest.cs new file mode 100644 index 00000000000..051cf960a40 --- /dev/null +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonProjectionSqliteTest.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.EntityFrameworkCore.Sqlite.Internal; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedJson; + +public class OwnedJsonProjectionSqliteTest(OwnedJsonSqliteFixture fixture, ITestOutputHelper testOutputHelper) + : OwnedJsonProjectionRelationalTestBase(fixture, testOutputHelper) +{ + public override Task SelectMany_related_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + => queryTrackingBehavior is QueryTrackingBehavior.TrackAll + ? Task.CompletedTask // Base test expects "can't track owned entities" exception, but with SQLite we get "no CROSS APPLY" + : AssertApplyNotSupported(() => base.SelectMany_related_collection(async, queryTrackingBehavior)); + + public override Task SelectMany_required_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + => queryTrackingBehavior is QueryTrackingBehavior.TrackAll + ? Task.CompletedTask // Base test expects "can't track owned entities" exception, but with SQLite we get "no CROSS APPLY" + : AssertApplyNotSupported(() => base.SelectMany_required_related_nested_collection(async, queryTrackingBehavior)); + + public override Task SelectMany_optional_related_nested_collection(bool async, QueryTrackingBehavior queryTrackingBehavior) + => queryTrackingBehavior is QueryTrackingBehavior.TrackAll + ? Task.CompletedTask // Base test expects "can't track owned entities" exception, but with SQLite we get "no CROSS APPLY" + : AssertApplyNotSupported(() => base.SelectMany_optional_related_nested_collection(async, queryTrackingBehavior)); + + public override Task Select_subquery_required_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + => queryTrackingBehavior is QueryTrackingBehavior.TrackAll + ? Task.CompletedTask // Base test expects "can't track owned entities" exception, but with SQLite we get "no CROSS APPLY" + : AssertApplyNotSupported(() => base.Select_subquery_required_related_FirstOrDefault(async, queryTrackingBehavior)); + + public override Task Select_subquery_optional_related_FirstOrDefault(bool async, QueryTrackingBehavior queryTrackingBehavior) + => queryTrackingBehavior is QueryTrackingBehavior.TrackAll + ? Task.CompletedTask // Base test expects "can't track owned entities" exception, but with SQLite we get "no CROSS APPLY" + : AssertApplyNotSupported(() => base.Select_subquery_optional_related_FirstOrDefault(async, queryTrackingBehavior)); + + private static async Task AssertApplyNotSupported(Func query) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync(query)) + .Message); +} diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonSqliteFixture.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonSqliteFixture.cs new file mode 100644 index 00000000000..fa76ed022a4 --- /dev/null +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedJson/OwnedJsonSqliteFixture.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedJson; + +public class OwnedJsonSqliteFixture : OwnedJsonRelationalFixtureBase +{ + protected override ITestStoreFactory TestStoreFactory + => SqliteTestStoreFactory.Instance; +} diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsCollectionSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsCollectionSqliteTest.cs new file mode 100644 index 00000000000..a3fcaa0a452 --- /dev/null +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsCollectionSqliteTest.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Data.Sqlite; + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; + +public class OwnedNavigationsCollectionSqliteTest(OwnedNavigationsSqliteFixture fixture, ITestOutputHelper testOutputHelper) + : OwnedNavigationsCollectionRelationalTestBase(fixture, testOutputHelper) +{ + // TODO: #36296 + public override Task Index_column(bool async) + => Assert.ThrowsAsync(() => base.Index_column(async)); +} diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsMiscellaneousSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsMiscellaneousSqliteTest.cs new file mode 100644 index 00000000000..c1d032a4617 --- /dev/null +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedNavigationsMiscellaneousSqliteTest.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; + +public class OwnedNavigationsMiscellaneousSqliteTest( + OwnedNavigationsSqliteFixture fixture, + ITestOutputHelper testOutputHelper) + : OwnedNavigationsMiscellaneousRelationalTestBase(fixture, testOutputHelper) +{ +} diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedRelationshipsSqliteFixture.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedRelationshipsSqliteFixture.cs index d853f146bee..676704ba078 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedRelationshipsSqliteFixture.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedNavigations/OwnedRelationshipsSqliteFixture.cs @@ -3,14 +3,11 @@ namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; -public class OwnedNavigationsSqliteFixture : OwnedNavigationsRelationalFixtureBase, ITestSqlLoggerFactory +public class OwnedNavigationsSqliteFixture : OwnedNavigationsRelationalFixtureBase { protected override ITestStoreFactory TestStoreFactory => SqliteTestStoreFactory.Instance; - public TestSqlLoggerFactory TestSqlLoggerFactory - => (TestSqlLoggerFactory)ListLoggerFactory; - public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) => base.AddOptions(builder.ConfigureWarnings(b => b.Ignore(SqliteEventId.CompositeKeyWithValueGeneration))); } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingMiscellaneousSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingMiscellaneousSqliteTest.cs new file mode 100644 index 00000000000..e1eb9302394 --- /dev/null +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingMiscellaneousSqliteTest.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedTableSplitting; + +public class OwnedTableSplittingMiscellaneousSqliteTest( + OwnedTableSplittingSqliteFixture fixture, + ITestOutputHelper testOutputHelper) + : OwnedTableSplittingMiscellaneousRelationalTestBase(fixture, testOutputHelper) +{ +} diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingProjectionSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingProjectionSqliteTest.cs index 318467b7062..1a3c8daca18 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingProjectionSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingProjectionSqliteTest.cs @@ -1,5 +1,5 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// All tests in OwnedNavigationsProjectionSqliteTest currently fail because of #26708 +// All tests in OwnedTableSplittingProjectionSqliteTest currently fail because of #26708 // (Stop generating composite keys for owned collections on SQLite) diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingRelationshipsSqliteFixture.cs b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingRelationshipsSqliteFixture.cs index 8e14bf0a46e..3f6c8a0a7c4 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingRelationshipsSqliteFixture.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/Relationships/OwnedTableSplitting/OwnedTableSplittingRelationshipsSqliteFixture.cs @@ -3,14 +3,11 @@ namespace Microsoft.EntityFrameworkCore.Query.Relationships.OwnedTableSplitting; -public class OwnedTableSplittingSqliteFixture : OwnedTableSplittingRelationalFixtureBase, ITestSqlLoggerFactory +public class OwnedTableSplittingSqliteFixture : OwnedTableSplittingRelationalFixtureBase { protected override ITestStoreFactory TestStoreFactory => SqliteTestStoreFactory.Instance; - public TestSqlLoggerFactory TestSqlLoggerFactory - => (TestSqlLoggerFactory)ListLoggerFactory; - public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) => base.AddOptions(builder.ConfigureWarnings(b => b.Ignore(SqliteEventId.CompositeKeyWithValueGeneration))); } diff --git a/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs b/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs index 5a16eb2a04f..16c5458b672 100644 --- a/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.EntityFrameworkCore.Query.Relationships.JsonOwnedNavigations; +using Microsoft.EntityFrameworkCore.Query.Relationships.OwnedJson; using Microsoft.EntityFrameworkCore.Query.Relationships.OwnedNavigations; using Microsoft.EntityFrameworkCore.Query.Relationships.OwnedTableSplitting; @@ -23,7 +23,7 @@ public class SqliteComplianceTest : RelationalComplianceTestBase // (Stop generating composite keys for owned collections on SQLite) typeof(OwnedNavigationsProjectionTestBase<>), typeof(OwnedNavigationsProjectionRelationalTestBase<>), - typeof(JsonOwnedNavigationsProjectionRelationalTestBase<>), + typeof(OwnedJsonProjectionRelationalTestBase<>), typeof(OwnedTableSplittingProjectionRelationalTestBase<>), typeof(ComplexCollectionJsonUpdateTestBase<>) // issue #31252 }; diff --git a/test/EFCore.Tests/ChangeTracking/Internal/ChangeDetectorTest.cs b/test/EFCore.Tests/ChangeTracking/Internal/ChangeDetectorTest.cs index c198ca80ef2..1c9113d757d 100644 --- a/test/EFCore.Tests/ChangeTracking/Internal/ChangeDetectorTest.cs +++ b/test/EFCore.Tests/ChangeTracking/Internal/ChangeDetectorTest.cs @@ -2131,8 +2131,8 @@ public override void AttachGraph( } private class TestRelationshipListener(IEntityGraphAttacher attacher) : NavigationFixer( - attacher, new EntityMaterializerSource( - new EntityMaterializerSourceDependencies(Enumerable.Empty()))) + attacher, new StructuralTypeMaterializerSource( + new StructuralTypeMaterializerSourceDependencies(Enumerable.Empty()))) { public Tuple, IEnumerable, object, object> KeyChange { diff --git a/test/EFCore.Tests/DbContextTest.Services.cs b/test/EFCore.Tests/DbContextTest.Services.cs index 44ba7585df7..58c02aa792e 100644 --- a/test/EFCore.Tests/DbContextTest.Services.cs +++ b/test/EFCore.Tests/DbContextTest.Services.cs @@ -829,11 +829,11 @@ public void Can_get_replaced_singleton_service_from_scoped_configuration() { var provider = new ServiceCollection() .AddEntityFrameworkInMemoryDatabase() - .AddSingleton() + .AddSingleton() .BuildServiceProvider(validateScopes: true); using var context = new EarlyLearningCenter(provider); - Assert.IsType(context.GetService()); + Assert.IsType(context.GetService()); } [ComplexType] @@ -950,8 +950,8 @@ protected internal override void OnModelCreating(ModelBuilder modelBuilder) } } - private class FakeEntityMaterializerSource(EntityMaterializerSourceDependencies dependencies) - : EntityMaterializerSource(dependencies); + private class FakeEntityMaterializerSource(StructuralTypeMaterializerSourceDependencies dependencies) + : StructuralTypeMaterializerSource(dependencies); private class FakeModelSource : IModelSource { diff --git a/test/EFCore.Tests/Query/EntityMaterializerSourceTest.cs b/test/EFCore.Tests/Query/EntityMaterializerSourceTest.cs index 59ff9eb5dcc..4551fd36bd1 100644 --- a/test/EFCore.Tests/Query/EntityMaterializerSourceTest.cs +++ b/test/EFCore.Tests/Query/EntityMaterializerSourceTest.cs @@ -19,21 +19,19 @@ public class EntityMaterializerSourceTest public void Throws_for_abstract_types() { var entityType = CreateConventionalModelBuilder().Model.AddEntityType(typeof(SomeAbstractEntity)); - var source = (IEntityMaterializerSource)new EntityMaterializerSource( - new EntityMaterializerSourceDependencies([])); + var source = (IStructuralTypeMaterializerSource)new StructuralTypeMaterializerSource( + new StructuralTypeMaterializerSourceDependencies([])); Assert.Equal( CoreStrings.CannotMaterializeAbstractType(nameof(SomeAbstractEntity)), Assert.Throws( () => source.CreateMaterializeExpression( - new EntityMaterializerSourceParameters((IEntityType)entityType, "", null), null!)) + new StructuralTypeMaterializerSourceParameters((IEntityType)entityType, "", null), null!)) .Message); } - [ConditionalTheory] - [InlineData(false)] - [InlineData(true)] - public void Can_create_materializer_for_entity_with_constructor_properties(bool useParameters) + [ConditionalFact] + public void Can_create_materializer_for_entity_with_constructor_properties() { using var context = new SomeEntityContext( b => @@ -52,9 +50,7 @@ public void Can_create_materializer_for_entity_with_constructor_properties(bool var entityType = context.Model.FindEntityType(typeof(SomeEntity))!; - var factory = GetMaterializer( - new EntityMaterializerSource( - new EntityMaterializerSourceDependencies([])), entityType, useParameters); + var factory = GetMaterializer(new StructuralTypeMaterializerSource(new StructuralTypeMaterializerSourceDependencies([])), entityType); var gu = Guid.NewGuid(); var entity = (SomeEntity)factory( @@ -74,10 +70,8 @@ public void Can_create_materializer_for_entity_with_constructor_properties(bool Assert.False(entity.GooSetterCalled); } - [ConditionalTheory] - [InlineData(false)] - [InlineData(true)] - public void Can_create_materializer_for_entity_with_factory_method(bool useParameters) + [ConditionalFact] + public void Can_create_materializer_for_entity_with_factory_method() { using var context = new SomeEntityContext( b => @@ -96,9 +90,7 @@ public void Can_create_materializer_for_entity_with_factory_method(bool useParam var entityType = context.Model.FindEntityType(typeof(SomeEntity))!; - var factory = GetMaterializer( - new EntityMaterializerSource( - new EntityMaterializerSourceDependencies([])), entityType, useParameters); + var factory = GetMaterializer(new StructuralTypeMaterializerSource(new StructuralTypeMaterializerSourceDependencies([])), entityType); var gu = Guid.NewGuid(); var entity = (SomeEntity)factory( @@ -118,10 +110,8 @@ public void Can_create_materializer_for_entity_with_factory_method(bool useParam Assert.False(entity.GooSetterCalled); } - [ConditionalTheory] - [InlineData(false)] - [InlineData(true)] - public void Can_create_materializer_for_entity_with_factory_method_with_object_array(bool useParameters) + [ConditionalFact] + public void Can_create_materializer_for_entity_with_factory_method_with_object_array() { using var context = new SomeEntityContext( b => @@ -144,9 +134,7 @@ public void Can_create_materializer_for_entity_with_factory_method_with_object_a var entityType = context.Model.FindEntityType(typeof(SomeEntity))!; - var factory = GetMaterializer( - new EntityMaterializerSource( - new EntityMaterializerSourceDependencies([])), entityType, useParameters); + var factory = GetMaterializer(new StructuralTypeMaterializerSource(new StructuralTypeMaterializerSourceDependencies([])), entityType); var gu = Guid.NewGuid(); var entity = (SomeEntity)factory( @@ -166,10 +154,8 @@ public void Can_create_materializer_for_entity_with_factory_method_with_object_a Assert.False(entity.GooSetterCalled); } - [ConditionalTheory] - [InlineData(false)] - [InlineData(true)] - public void Can_create_materializer_for_entity_with_instance_factory_method(bool useParameters) + [ConditionalFact] + public void Can_create_materializer_for_entity_with_instance_factory_method() { using var context = new SomeEntityContext( b => @@ -185,9 +171,7 @@ public void Can_create_materializer_for_entity_with_instance_factory_method(bool var entityType = context.Model.FindEntityType(typeof(SomeEntity))!; - var factory = GetMaterializer( - new EntityMaterializerSource( - new EntityMaterializerSourceDependencies([])), entityType, useParameters); + var factory = GetMaterializer(new StructuralTypeMaterializerSource(new StructuralTypeMaterializerSourceDependencies([])), entityType); var gu = Guid.NewGuid(); var entity = (SomeEntity)factory( @@ -215,17 +199,13 @@ public object Create(IEntityType entityType) => Activator.CreateInstance(entityType.ClrType); } - [ConditionalTheory] - [InlineData(false)] - [InlineData(true)] - public void Can_create_materializer_for_entity_with_auto_properties(bool useParameters) + [ConditionalFact] + public void Can_create_materializer_for_entity_with_auto_properties() { using var context = new SomeEntityContext(b => b.Entity()); var entityType = context.Model.FindEntityType(typeof(SomeEntity)); - var factory = GetMaterializer( - new EntityMaterializerSource( - new EntityMaterializerSourceDependencies([])), entityType, useParameters); + var factory = GetMaterializer(new StructuralTypeMaterializerSource(new StructuralTypeMaterializerSourceDependencies([])), entityType); var gu = Guid.NewGuid(); var entity = (SomeEntity)factory( @@ -240,10 +220,8 @@ public void Can_create_materializer_for_entity_with_auto_properties(bool usePara Assert.Equal(SomeEnum.EnumValue, entity.MaybeEnum); } - [ConditionalTheory] - [InlineData(false)] - [InlineData(true)] - public void Can_create_materializer_for_entity_with_fields(bool useParameters) + [ConditionalFact] + public void Can_create_materializer_for_entity_with_fields() { using var context = new SomeEntityContext( b => b.Entity( @@ -260,9 +238,7 @@ public void Can_create_materializer_for_entity_with_fields(bool useParameters) var entityType = context.Model.FindEntityType(typeof(SomeEntityWithFields)); - var factory = GetMaterializer( - new EntityMaterializerSource( - new EntityMaterializerSourceDependencies([])), entityType, useParameters); + var factory = GetMaterializer(new StructuralTypeMaterializerSource(new StructuralTypeMaterializerSourceDependencies([])), entityType); var gu = Guid.NewGuid(); var entity = (SomeEntityWithFields)factory( @@ -277,10 +253,8 @@ public void Can_create_materializer_for_entity_with_fields(bool useParameters) Assert.Null(entity.MaybeEnum); } - [ConditionalTheory] - [InlineData(false)] - [InlineData(true)] - public void Can_read_nulls(bool useParameters) + [ConditionalFact] + public void Can_read_nulls() { using var context = new SomeEntityContext( b => b.Entity( @@ -292,9 +266,7 @@ public void Can_read_nulls(bool useParameters) var entityType = context.Model.FindEntityType(typeof(SomeEntity)); - var factory = GetMaterializer( - new EntityMaterializerSource( - new EntityMaterializerSourceDependencies([])), entityType, useParameters); + var factory = GetMaterializer(new StructuralTypeMaterializerSource(new StructuralTypeMaterializerSourceDependencies([])), entityType); var entity = (SomeEntity)factory( new MaterializationContext( @@ -306,10 +278,8 @@ public void Can_read_nulls(bool useParameters) Assert.Null(entity.Goo); } - [ConditionalTheory] - [InlineData(false)] - [InlineData(true)] - public void Can_create_materializer_for_entity_ignoring_shadow_fields(bool useParameters) + [ConditionalFact] + public void Can_create_materializer_for_entity_ignoring_shadow_fields() { using var context = new SomeEntityContext( b => b.Entity( @@ -327,9 +297,7 @@ public void Can_create_materializer_for_entity_ignoring_shadow_fields(bool usePa var entityType = context.Model.FindEntityType(typeof(SomeEntity)); - var factory = GetMaterializer( - new EntityMaterializerSource( - new EntityMaterializerSourceDependencies([])), entityType, useParameters); + var factory = GetMaterializer(new StructuralTypeMaterializerSource(new StructuralTypeMaterializerSourceDependencies([])), entityType); var gu = Guid.NewGuid(); var entity = (SomeEntity)factory( @@ -364,7 +332,7 @@ public void GetEmptyMaterializer_Create_instance_with_parameterless_constructor( using var context = new FactoryContext(); var entityType = context.Model.FindEntityType(typeof(Parameterless))!; - var source = new EntityMaterializerSource(new EntityMaterializerSourceDependencies([])); + var source = new StructuralTypeMaterializerSource(new StructuralTypeMaterializerSourceDependencies([])); var instance1 = source.GetEmptyMaterializer(entityType)(new MaterializationContext(ValueBuffer.Empty, context)); var instance2 = source.GetEmptyMaterializer(entityType)(new MaterializationContext(ValueBuffer.Empty, context)); @@ -379,7 +347,7 @@ public void GetEmptyMaterializer_Create_instance_with_lazy_loader() using var context = new FactoryContext(); var entityType = context.Model.FindEntityType(typeof(WithLazyLoader))!; - var source = new EntityMaterializerSource(new EntityMaterializerSourceDependencies([])); + var source = new StructuralTypeMaterializerSource(new StructuralTypeMaterializerSourceDependencies([])); var instance1 = source.GetEmptyMaterializer(entityType)(new MaterializationContext(ValueBuffer.Empty, context)); var instance2 = source.GetEmptyMaterializer(entityType)(new MaterializationContext(ValueBuffer.Empty, context)); @@ -396,7 +364,7 @@ public void GetEmptyMaterializer_Create_instance_with_lazy_loading_delegate() using var context = new FactoryContext(); var entityType = context.Model.FindEntityType(typeof(WithLazyLoaderDelegate))!; - var source = new EntityMaterializerSource(new EntityMaterializerSourceDependencies([])); + var source = new StructuralTypeMaterializerSource(new StructuralTypeMaterializerSourceDependencies([])); var instance1 = source.GetEmptyMaterializer(entityType)(new MaterializationContext(ValueBuffer.Empty, context)); var instance2 = source.GetEmptyMaterializer(entityType)(new MaterializationContext(ValueBuffer.Empty, context)); @@ -413,7 +381,7 @@ public void GetEmptyMaterializer_Create_instance_with_entity_type() using var context = new FactoryContext(); var entityType = context.Model.FindEntityType(typeof(WithEntityType))!; - var source = new EntityMaterializerSource(new EntityMaterializerSourceDependencies([])); + var source = new StructuralTypeMaterializerSource(new StructuralTypeMaterializerSourceDependencies([])); var instance1 = source.GetEmptyMaterializer(entityType)(new MaterializationContext(ValueBuffer.Empty, context)); var instance2 = source.GetEmptyMaterializer(entityType)(new MaterializationContext(ValueBuffer.Empty, context)); @@ -430,7 +398,7 @@ public void GetEmptyMaterializer_Create_instance_with_context() using var context = new FactoryContext(); var entityType = context.Model.FindEntityType(typeof(WithContext))!; - var source = new EntityMaterializerSource(new EntityMaterializerSourceDependencies([])); + var source = new StructuralTypeMaterializerSource(new StructuralTypeMaterializerSourceDependencies([])); var instance1 = source.GetEmptyMaterializer(entityType)(new MaterializationContext(ValueBuffer.Empty, context)); var instance2 = source.GetEmptyMaterializer(entityType)(new MaterializationContext(ValueBuffer.Empty, context)); @@ -447,7 +415,7 @@ public void GetEmptyMaterializer_Create_instance_with_service_and_with_propertie using var context = new FactoryContext(); var entityType = context.Model.FindEntityType(typeof(WithServiceAndWithProperties))!; - var source = new EntityMaterializerSource(new EntityMaterializerSourceDependencies([])); + var source = new StructuralTypeMaterializerSource(new StructuralTypeMaterializerSourceDependencies([])); var instance1 = source.GetEmptyMaterializer(entityType)(new MaterializationContext(ValueBuffer.Empty, context)); var instance2 = source.GetEmptyMaterializer(entityType)(new MaterializationContext(ValueBuffer.Empty, context)); @@ -464,7 +432,7 @@ public void GetEmptyMaterializer_Create_instance_with_parameterless_and_with_pro using var context = new FactoryContext(); var entityType = context.Model.FindEntityType(typeof(ParameterlessAndWithProperties))!; - var source = new EntityMaterializerSource(new EntityMaterializerSourceDependencies([])); + var source = new StructuralTypeMaterializerSource(new StructuralTypeMaterializerSourceDependencies([])); var instance1 = source.GetEmptyMaterializer(entityType)(new MaterializationContext(ValueBuffer.Empty, context)); var instance2 = source.GetEmptyMaterializer(entityType)(new MaterializationContext(ValueBuffer.Empty, context)); @@ -479,7 +447,7 @@ public void GetEmptyMaterializer_Throws_for_constructor_with_properties() using var context = new FactoryContext(); var entityType = context.Model.FindEntityType(typeof(WithProperties))!; - var source = new EntityMaterializerSource(new EntityMaterializerSourceDependencies([])); + var source = new StructuralTypeMaterializerSource(new StructuralTypeMaterializerSourceDependencies([])); Assert.Equal( CoreStrings.NoParameterlessConstructor(nameof(WithProperties)), @@ -578,16 +546,12 @@ private static readonly ParameterExpression _contextParameter = Expression.Parameter(typeof(MaterializationContext), "materializationContext"); public virtual Func GetMaterializer( - IEntityMaterializerSource source, - IReadOnlyEntityType entityType, - bool useParameters) + IStructuralTypeMaterializerSource source, + IReadOnlyEntityType entityType) => Expression.Lambda>( - useParameters - ? source.CreateMaterializeExpression( - new EntityMaterializerSourceParameters((IEntityType)entityType, "instance", null), _contextParameter) -#pragma warning disable CS0618 - : source.CreateMaterializeExpression((IEntityType)entityType, "instance", _contextParameter), -#pragma warning restore CS0618 + source.CreateMaterializeExpression( + new StructuralTypeMaterializerSourceParameters((IEntityType)entityType, "instance", null), + _contextParameter), _contextParameter) .Compile(); diff --git a/test/EFCore.Tests/TestUtilities/FakeStateManager.cs b/test/EFCore.Tests/TestUtilities/FakeStateManager.cs index 396fb753cd1..f45cd01865d 100644 --- a/test/EFCore.Tests/TestUtilities/FakeStateManager.cs +++ b/test/EFCore.Tests/TestUtilities/FakeStateManager.cs @@ -164,7 +164,7 @@ public void ChangingState(InternalEntityEntry entry, EntityState newState) public IValueGenerationManager ValueGenerationManager => throw new NotImplementedException(); - public IEntityMaterializerSource EntityMaterializerSource { get; } + public IStructuralTypeMaterializerSource EntityMaterializerSource { get; } public InternalEntityEntry StartTracking(InternalEntityEntry entry) => throw new NotImplementedException();