diff --git a/src/EFCore/Properties/CoreStrings.Designer.cs b/src/EFCore/Properties/CoreStrings.Designer.cs
index 97c5c8eb221..56e0ed21910 100644
--- a/src/EFCore/Properties/CoreStrings.Designer.cs
+++ b/src/EFCore/Properties/CoreStrings.Designer.cs
@@ -1319,12 +1319,12 @@ public static string AmbiguousForeignKeyPropertyCandidates([CanBeNull] object fi
firstDependentToPrincipalNavigationSpecification, firstPrincipalToDependentNavigationSpecification, secondDependentToPrincipalNavigationSpecification, secondPrincipalToDependentNavigationSpecification, foreignKeyProperties);
///
- /// The {methodName} property lambda expression '{includeLambdaExpression}' is invalid. The expression should represent a property access: 't => t.MyProperty'. To target navigations declared on derived types, specify an explicitly typed lambda parameter of the target type, E.g. '(Derived d) => d.MyProperty'. For more information on including related data, see http://go.microsoft.com/fwlink/?LinkID=746393.
+ /// The expression '{expression}' is invalid inside Include operation. The expression should represent a property access: 't => t.MyProperty'. To target navigations declared on derived types, specify an explicitly typed lambda parameter of the target type, E.g. '(Derived d) => d.MyProperty'. Collection navigation access can be filtered by composing Where, OrderBy(Descending), ThenBy(Descending), Skip or Take operations. For more information on including related data, see http://go.microsoft.com/fwlink/?LinkID=746393.
///
- public static string InvalidIncludeLambdaExpression([CanBeNull] object methodName, [CanBeNull] object includeLambdaExpression)
+ public static string InvalidIncludeExpression([CanBeNull] object expression)
=> string.Format(
- GetString("InvalidIncludeLambdaExpression", nameof(methodName), nameof(includeLambdaExpression)),
- methodName, includeLambdaExpression);
+ GetString("InvalidIncludeExpression", nameof(expression)),
+ expression);
///
/// The corresponding CLR type for entity type '{entityType}' is not instantiable and there is no derived entity type in the model that corresponds to a concrete CLR type.
@@ -2422,6 +2422,14 @@ public static string InvalidLambdaExpressionInsideInclude
public static string IncludeOnNonEntity
=> GetString("IncludeOnNonEntity");
+ ///
+ /// Different filters '{filter1}' and '{filter2}' have been applied on the same included navigation. Only one unique filter per navigation is allowed. For more information on including related data, see http://go.microsoft.com/fwlink/?LinkID=746393.
+ ///
+ public static string MultipleFilteredIncludesOnSameNavigation([CanBeNull] object filter1, [CanBeNull] object filter2)
+ => string.Format(
+ GetString("MultipleFilteredIncludesOnSameNavigation", nameof(filter1), nameof(filter2)),
+ filter1, filter2);
+
///
/// Unable to convert queryable method to enumerable method.
///
diff --git a/src/EFCore/Properties/CoreStrings.resx b/src/EFCore/Properties/CoreStrings.resx
index 1848bc401fb..ce4e3f810db 100644
--- a/src/EFCore/Properties/CoreStrings.resx
+++ b/src/EFCore/Properties/CoreStrings.resx
@@ -747,8 +747,8 @@
Both relationships between '{firstDependentToPrincipalNavigationSpecification}' and '{firstPrincipalToDependentNavigationSpecification}' and between '{secondDependentToPrincipalNavigationSpecification}' and '{secondPrincipalToDependentNavigationSpecification}' could use {foreignKeyProperties} as the foreign key. To resolve this configure the foreign key properties explicitly on at least one of the relationships.
-
- The {methodName} property lambda expression '{includeLambdaExpression}' is invalid. The expression should represent a property access: 't => t.MyProperty'. To target navigations declared on derived types, specify an explicitly typed lambda parameter of the target type, E.g. '(Derived d) => d.MyProperty'. For more information on including related data, see http://go.microsoft.com/fwlink/?LinkID=746393.
+
+ The expression '{expression}' is invalid inside Include operation. The expression should represent a property access: 't => t.MyProperty'. To target navigations declared on derived types, specify an explicitly typed lambda parameter of the target type, E.g. '(Derived d) => d.MyProperty'. Collection navigation access can be filtered by composing Where, OrderBy(Descending), ThenBy(Descending), Skip or Take operations. For more information on including related data, see http://go.microsoft.com/fwlink/?LinkID=746393.
The corresponding CLR type for entity type '{entityType}' is not instantiable and there is no derived entity type in the model that corresponds to a concrete CLR type.
@@ -1290,6 +1290,9 @@
Include has been used on non entity queryable.
+
+ Different filters '{filter1}' and '{filter2}' have been applied on the same included navigation. Only one unique filter per navigation is allowed. For more information on including related data, see http://go.microsoft.com/fwlink/?LinkID=746393.
+
Unable to convert queryable method to enumerable method.
diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs
index c129c668529..fc6ca2aee97 100644
--- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs
+++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs
@@ -181,9 +181,8 @@ protected Expression ExpandNavigation(
var innerSource = (NavigationExpansionExpression)_navigationExpandingExpressionVisitor.Visit(innerQueryable);
if (entityReference.IncludePaths.ContainsKey(navigation))
{
- var innerIncludeTreeNode = entityReference.IncludePaths[navigation];
var innerEntityReference = (EntityReference)((NavigationTreeExpression)innerSource.PendingSelector).Value;
- innerEntityReference.SetIncludePaths(innerIncludeTreeNode);
+ innerEntityReference.SetIncludePaths(entityReference.IncludePaths[navigation]);
}
var innerSourceSequenceType = innerSource.Type.GetSequenceType();
@@ -532,6 +531,20 @@ private Expression ExpandIncludesHelper(Expression root, EntityReference entityR
included = ExpandIncludesHelper(included, innerEntityReference);
}
+ if (included is MaterializeCollectionNavigationExpression materializeCollectionNavigation)
+ {
+ var filterExpression = entityReference.IncludePaths[navigation].FilterExpression;
+ if (filterExpression != null)
+ {
+ var subquery = ReplacingExpressionVisitor.Replace(
+ filterExpression.Parameters[0],
+ materializeCollectionNavigation.Subquery,
+ filterExpression.Body);
+
+ included = materializeCollectionNavigation.Update(subquery);
+ }
+ }
+
result = new IncludeExpression(result, included, navigation);
}
diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs
index 8cee5661892..62045b12018 100644
--- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs
+++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs
@@ -84,6 +84,8 @@ protected class IncludeTreeNode : Dictionary
{
private EntityReference _entityReference;
+ public virtual LambdaExpression FilterExpression { get; set; }
+
public IncludeTreeNode(IEntityType entityType, EntityReference entityReference)
{
EntityType = entityType;
diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs
index 6d5e6871b16..1297357ea58 100644
--- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs
+++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs
@@ -32,6 +32,19 @@ private static readonly PropertyInfo _queryContextContextPropertyInfo
{ QueryableMethods.LastWithPredicate, QueryableMethods.LastWithoutPredicate },
{ QueryableMethods.LastOrDefaultWithPredicate, QueryableMethods.LastOrDefaultWithoutPredicate }
};
+
+ private static readonly List _supportedFilteredIncludeOperations = new List
+ {
+ QueryableMethods.Where,
+ QueryableMethods.OrderBy,
+ QueryableMethods.OrderByDescending,
+ QueryableMethods.ThenBy,
+ QueryableMethods.ThenByDescending,
+ QueryableMethods.Skip,
+ QueryableMethods.Take,
+ QueryableMethods.AsQueryable
+ };
+
private readonly QueryTranslationPreprocessor _queryTranslationPreprocessor;
private readonly QueryCompilationContext _queryCompilationContext;
private readonly PendingSelectorExpandingExpressionVisitor _pendingSelectorExpandingExpressionVisitor;
@@ -786,12 +799,28 @@ private NavigationExpansionExpression ProcessInclude(NavigationExpansionExpressi
? entityReference.LastIncludeTreeNode
: entityReference.IncludePaths;
var includeLambda = expression.UnwrapLambdaFromQuote();
- var lastIncludeTree = PopulateIncludeTree(currentIncludeTreeNode, includeLambda.Body);
+
+ var (result, filterExpression) = ExtractIncludeFilter(includeLambda.Body, includeLambda.Body);
+ var lastIncludeTree = PopulateIncludeTree(currentIncludeTreeNode, result);
if (lastIncludeTree == null)
{
throw new InvalidOperationException(CoreStrings.InvalidLambdaExpressionInsideInclude);
}
+ if (filterExpression != null)
+ {
+ if (lastIncludeTree.FilterExpression != null
+ && !ExpressionEqualityComparer.Instance.Equals(filterExpression, lastIncludeTree.FilterExpression))
+ {
+ throw new InvalidOperationException(
+ CoreStrings.MultipleFilteredIncludesOnSameNavigation(
+ FormatFilter(filterExpression.Body).Print(),
+ FormatFilter(lastIncludeTree.FilterExpression.Body).Print()));
+ }
+
+ lastIncludeTree.FilterExpression = filterExpression;
+ }
+
entityReference.SetLastInclude(lastIncludeTree);
}
@@ -799,6 +828,63 @@ private NavigationExpansionExpression ProcessInclude(NavigationExpansionExpressi
}
throw new InvalidOperationException(CoreStrings.IncludeOnNonEntity);
+
+ (Expression result, LambdaExpression filterExpression) ExtractIncludeFilter(Expression currentExpression, Expression includeExpression)
+ {
+ if (currentExpression is MemberExpression)
+ {
+ return (currentExpression, default(LambdaExpression));
+ }
+
+ if (currentExpression is MethodCallExpression methodCallExpression)
+ {
+ if (!methodCallExpression.Method.IsGenericMethod
+ || !_supportedFilteredIncludeOperations.Contains(methodCallExpression.Method.GetGenericMethodDefinition()))
+ {
+ throw new InvalidOperationException(CoreStrings.InvalidIncludeExpression(includeExpression));
+ }
+
+ var (result, filterExpression) = ExtractIncludeFilter(methodCallExpression.Arguments[0], includeExpression);
+ if (filterExpression == null)
+ {
+ var prm = Expression.Parameter(result.Type);
+ filterExpression = Expression.Lambda(prm, prm);
+ }
+
+ var arguments = new List();
+ arguments.Add(filterExpression.Body);
+ arguments.AddRange(methodCallExpression.Arguments.Skip(1));
+ filterExpression = Expression.Lambda(
+ methodCallExpression.Update(methodCallExpression.Object, arguments),
+ filterExpression.Parameters);
+
+ return (result, filterExpression);
+ }
+
+ throw new InvalidOperationException(CoreStrings.InvalidIncludeExpression(includeExpression));
+ }
+
+ Expression FormatFilter(Expression expression)
+ {
+ if (expression is MethodCallExpression methodCallExpression
+ && methodCallExpression.Method.IsGenericMethod
+ && _supportedFilteredIncludeOperations.Contains(methodCallExpression.Method.GetGenericMethodDefinition()))
+ {
+ if (methodCallExpression.Method.GetGenericMethodDefinition() == QueryableMethods.AsQueryable)
+ {
+ return Expression.Parameter(expression.Type, "navigation");
+ }
+
+ var arguments = new List();
+ var source = FormatFilter(methodCallExpression.Arguments[0]);
+ arguments.Add(source);
+ arguments.AddRange(methodCallExpression.Arguments.Skip(1));
+
+ return methodCallExpression.Update(methodCallExpression.Object, arguments);
+ }
+
+ return expression;
+ }
}
private NavigationExpansionExpression ProcessJoin(
@@ -1474,8 +1560,10 @@ private IncludeTreeNode PopulateIncludeTree(IncludeTreeNode includeTreeNode, Exp
if (navigation != null)
{
var addedNode = innerIncludeTreeNode.AddNavigation(navigation);
+
// This is to add eager Loaded navigations when owner type is included.
PopulateEagerLoadedNavigations(addedNode);
+
return addedNode;
}
diff --git a/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs
index 2f9a7699522..9e5788325f7 100644
--- a/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs
@@ -4980,5 +4980,461 @@ public virtual Task Contains_over_optional_navigation_with_null_entity_reference
}),
elementSorter: e => (e.Name, e.OptionalName, e.Contains));
}
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Filtered_include_basic_Where(bool async)
+ {
+ return AssertIncludeQuery(
+ async,
+ ss => ss.Set().Include(l1 => l1.OneToMany_Optional1.Where(l2 => l2.Id > 5)),
+ new List
+ {
+ new ExpectedFilteredInclude(
+ e => e.OneToMany_Optional1,
+ "OneToMany_Optional1",
+ includeFilter: x => x.Where(l2 => l2.Id > 5))
+ });
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Filtered_include_basic_OrderBy_Take(bool async)
+ {
+ return AssertIncludeQuery(
+ async,
+ ss => ss.Set().Include(l1 => l1.OneToMany_Optional1.OrderBy(x => x.Name).Take(3)),
+ new List
+ {
+ new ExpectedFilteredInclude(
+ e => e.OneToMany_Optional1,
+ "OneToMany_Optional1",
+ includeFilter: x => x.OrderBy(x => x.Name).Take(3))
+ });
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Filtered_include_basic_OrderBy_Skip(bool async)
+ {
+ return AssertIncludeQuery(
+ async,
+ ss => ss.Set().Include(l1 => l1.OneToMany_Optional1.OrderBy(x => x.Name).Skip(1)),
+ new List
+ {
+ new ExpectedFilteredInclude(
+ e => e.OneToMany_Optional1,
+ "OneToMany_Optional1",
+ includeFilter: x => x.OrderBy(x => x.Name).Skip(1))
+ });
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Filtered_include_basic_OrderBy_Skip_Take(bool async)
+ {
+ return AssertIncludeQuery(
+ async,
+ ss => ss.Set().Include(l1 => l1.OneToMany_Optional1.OrderBy(x => x.Name).Skip(1).Take(3)),
+ new List
+ {
+ new ExpectedFilteredInclude(
+ e => e.OneToMany_Optional1,
+ "OneToMany_Optional1",
+ includeFilter: x => x.OrderBy(x => x.Name).Skip(1).Take(3))
+ });
+ }
+
+ [ConditionalFact]
+ public virtual void Filtered_include_Skip_without_OrderBy()
+ {
+ using var ctx = CreateContext();
+ var query = ctx.LevelOne.Include(l1 => l1.OneToMany_Optional1.Skip(1));
+ var result = query.ToList();
+ }
+
+ [ConditionalFact]
+ public virtual void Filtered_include_Take_without_OrderBy()
+ {
+ using var ctx = CreateContext();
+ var query = ctx.LevelOne.Include(l1 => l1.OneToMany_Optional1.Skip(1));
+ var result = query.ToList();
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Filtered_include_on_ThenInclude(bool async)
+ {
+ return AssertIncludeQuery(
+ async,
+ ss => ss.Set()
+ .Include(l1 => l1.OneToOne_Optional_FK1)
+ .ThenInclude(l2 => l2.OneToMany_Optional2.Where(x => x.Name != "Foo").OrderBy(x => x.Name).Skip(1).Take(3)),
+ new List
+ {
+ new ExpectedInclude(e => e.OneToOne_Optional_FK1, "OneToOne_Optional_FK1"),
+ new ExpectedFilteredInclude(
+ e => e.OneToMany_Optional2,
+ "OneToMany_Optional2",
+ "OneToOne_Optional_FK1",
+ x => x.Where(x => x.Name != "Foo").OrderBy(x => x.Name).Skip(1).Take(3))
+ });
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Filtered_include_after_reference_navigation(bool async)
+ {
+ return AssertIncludeQuery(
+ async,
+ ss => ss.Set()
+ .Include(l1 => l1.OneToOne_Optional_FK1.OneToMany_Optional2.Where(x => x.Name != "Foo").OrderBy(x => x.Name).Skip(1).Take(3)),
+ new List
+ {
+ new ExpectedInclude(e => e.OneToOne_Optional_FK1, "OneToOne_Optional_FK1"),
+ new ExpectedFilteredInclude(
+ e => e.OneToMany_Optional2,
+ "OneToMany_Optional2",
+ "OneToOne_Optional_FK1",
+ x => x.Where(x => x.Name != "Foo").OrderBy(x => x.Name).Skip(1).Take(3))
+ });
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Filtered_include_after_different_filtered_include_same_level(bool async)
+ {
+ return AssertIncludeQuery(
+ async,
+ ss => ss.Set()
+ .Include(l1 => l1.OneToMany_Optional1.Where(x => x.Name != "Foo").OrderBy(x => x.Name).Take(3))
+ .Include(l1 => l1.OneToMany_Required1.Where(x => x.Name != "Bar").OrderByDescending(x => x.Name).Skip(1)),
+ new List
+ {
+ new ExpectedFilteredInclude(
+ e => e.OneToMany_Optional1,
+ "OneToMany_Optional1",
+ includeFilter: x => x.Where(x => x.Name != "Foo").OrderBy(x => x.Name).Take(3)),
+ new ExpectedFilteredInclude(
+ e => e.OneToMany_Required1,
+ "OneToMany_Required1",
+ includeFilter: x => x.Where(x => x.Name != "Bar").OrderByDescending(x => x.Name).Skip(1))
+ });
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Filtered_include_after_different_filtered_include_different_level(bool async)
+ {
+ return AssertIncludeQuery(
+ async,
+ ss => ss.Set()
+ .Include(l1 => l1.OneToMany_Optional1.Where(x => x.Name != "Foo").OrderBy(x => x.Name).Take(3))
+ .ThenInclude(l2 => l2.OneToMany_Required2.Where(x => x.Name != "Bar").OrderByDescending(x => x.Name).Skip(1)),
+ new List
+ {
+ new ExpectedFilteredInclude(
+ e => e.OneToMany_Optional1,
+ "OneToMany_Optional1",
+ includeFilter: x => x.Where(x => x.Name != "Foo").OrderBy(x => x.Name).Take(3)),
+ new ExpectedFilteredInclude(
+ e => e.OneToMany_Required2,
+ "OneToMany_Required2",
+ "OneToMany_Optional1",
+ includeFilter: x => x.Where(x => x.Name != "Bar").OrderByDescending(x => x.Name).Skip(1))
+ });
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual async Task Filtered_include_different_filter_set_on_same_navigation_twice(bool async)
+ {
+ var message = (await Assert.ThrowsAsync(
+ () => AssertQuery(
+ async,
+ ss => ss.Set()
+ .Include(l1 => l1.OneToMany_Optional1.Where(x => x.Name != "Foo").OrderBy(x => x.Id).Take(3))
+ .Include(l1 => l1.OneToMany_Optional1.Where(x => x.Name != "Bar").OrderByDescending(x => x.Name).Take(3))))).Message;
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual async Task Filtered_include_different_filter_set_on_same_navigation_twice_multi_level(bool async)
+ {
+ var message = (await Assert.ThrowsAsync(
+ () => AssertQuery(
+ async,
+ ss => ss.Set()
+ .Include(l1 => l1.OneToMany_Optional1.Where(x => x.Name != "Foo")).ThenInclude(l2 => l2.OneToMany_Optional2)
+ .Include(l1 => l1.OneToMany_Optional1.Where(x => x.Name != "Bar")).ThenInclude(l2 => l2.OneToOne_Required_FK2)))).Message;
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Filtered_include_same_filter_set_on_same_navigation_twice(bool async)
+ {
+ return AssertIncludeQuery(
+ async,
+ ss => ss.Set()
+ .Include(l1 => l1.OneToMany_Optional1.Where(x => x.Name != "Foo").OrderByDescending(x => x.Id).Take(2))
+ .Include(l1 => l1.OneToMany_Optional1.Where(x => x.Name != "Foo").OrderByDescending(x => x.Id).Take(2)),
+ new List
+ {
+ new ExpectedFilteredInclude(
+ e => e.OneToMany_Optional1,
+ "OneToMany_Optional1",
+ includeFilter: x => x.Where(x => x.Name != "Foo").OrderByDescending(x => x.Id).Take(2)),
+ });
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Filtered_include_same_filter_set_on_same_navigation_twice_followed_by_ThenIncludes(bool async)
+ {
+ return AssertIncludeQuery(
+ async,
+ ss => ss.Set()
+ .Include(l1 => l1.OneToMany_Optional1.Where(x => x.Name != "Foo").OrderBy(x => x.Id).Take(2)).ThenInclude(l2 => l2.OneToMany_Optional2)
+ .Include(l1 => l1.OneToMany_Optional1.Where(x => x.Name != "Foo").OrderBy(x => x.Id).Take(2)).ThenInclude(l2 => l2.OneToOne_Required_FK2),
+ new List
+ {
+ new ExpectedFilteredInclude(
+ e => e.OneToMany_Optional1,
+ "OneToMany_Optional1",
+ includeFilter: x => x.Where(x => x.Name != "Foo").OrderBy(x => x.Id).Take(2)),
+ new ExpectedInclude(e => e.OneToMany_Optional2, "OneToMany_Optional1"),
+ new ExpectedInclude(e => e.OneToOne_Required_FK2, "OneToMany_Optional1"),
+ });
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Filtered_include_multiple_multi_level_includes_with_first_level_using_filter_include_on_one_of_the_chains_only(bool async)
+ {
+ return AssertIncludeQuery(
+ async,
+ ss => ss.Set()
+ .Include(l1 => l1.OneToMany_Optional1.Where(x => x.Name != "Foo").OrderBy(x => x.Id).Take(2)).ThenInclude(l2 => l2.OneToMany_Optional2)
+ .Include(l1 => l1.OneToMany_Optional1).ThenInclude(l2 => l2.OneToOne_Required_FK2),
+ new List
+ {
+ new ExpectedFilteredInclude(
+ e => e.OneToMany_Optional1,
+ "OneToMany_Optional1",
+ includeFilter: x => x.Where(x => x.Name != "Foo").OrderBy(x => x.Id).Take(2)),
+ new ExpectedInclude(e => e.OneToMany_Optional2, "OneToMany_Optional2", "OneToMany_Optional1"),
+ new ExpectedInclude(e => e.OneToOne_Required_FK2, "OneToOne_Required_FK2", "OneToMany_Optional1"),
+ });
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Filtered_include_and_non_filtered_include_on_same_navigation1(bool async)
+ {
+ return AssertIncludeQuery(
+ async,
+ ss => ss.Set()
+ .Include(l1 => l1.OneToMany_Optional1)
+ .Include(l1 => l1.OneToMany_Optional1.Where(x => x.Name != "Foo").OrderBy(x => x.Id).Take(3)),
+ new List
+ {
+ new ExpectedFilteredInclude(
+ e => e.OneToMany_Optional1,
+ "OneToMany_Optional1",
+ includeFilter: x => x.Where(x => x.Name != "Foo").OrderBy(x => x.Id).Take(3))
+ });
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Filtered_include_and_non_filtered_include_on_same_navigation2(bool async)
+ {
+ return AssertIncludeQuery(
+ async,
+ ss => ss.Set()
+ .Include(l1 => l1.OneToMany_Optional1.Where(x => x.Name != "Foo").OrderBy(x => x.Id).Take(3))
+ .Include(l1 => l1.OneToMany_Optional1),
+ new List
+ {
+ new ExpectedFilteredInclude(
+ e => e.OneToMany_Optional1,
+ "OneToMany_Optional1",
+ includeFilter: x => x.Where(x => x.Name != "Foo").OrderBy(x => x.Id).Take(3))
+ });
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Filtered_include_and_non_filtered_include_followed_by_then_include_on_same_navigation(bool async)
+ {
+ return AssertIncludeQuery(
+ async,
+ ss => ss.Set()
+ .Include(l1 => l1.OneToMany_Optional1.Where(x => x.Name != "Foo").OrderBy(x => x.Id).Take(1))
+ .Include(l1 => l1.OneToMany_Optional1).ThenInclude(l2 => l2.OneToOne_Optional_PK2.OneToMany_Optional3.Where(x => x.Id > 1)),
+ new List
+ {
+ new ExpectedFilteredInclude(
+ e => e.OneToMany_Optional1,
+ "OneToMany_Optional1",
+ includeFilter: x => x.Where(x => x.Name != "Foo").OrderBy(x => x.Id).Take(1)),
+ new ExpectedInclude(
+ e => e.OneToOne_Optional_PK2,
+ "OneToOne_Optional_PK2",
+ "OneToMany_Optional1"),
+ new ExpectedFilteredInclude(
+ e => e.OneToMany_Optional3,
+ "OneToMany_Optional3",
+ "OneToMany_Optional1.OneToOne_Optional_PK2",
+ includeFilter: x => x.Where(x => x.Id > 1)),
+ });
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Filtered_include_complex_three_level_with_middle_having_filter1(bool async)
+ {
+ return AssertIncludeQuery(
+ async,
+ ss => ss.Set()
+ .Include(l1 => l1.OneToMany_Optional1).ThenInclude(l2 => l2.OneToMany_Optional2.Where(x => x.Name != "Foo").OrderBy(x => x.Id).Take(1)).ThenInclude(l3 => l3.OneToMany_Optional3)
+ .Include(l1 => l1.OneToMany_Optional1).ThenInclude(l2 => l2.OneToMany_Optional2.Where(x => x.Name != "Foo").OrderBy(x => x.Id).Take(1)).ThenInclude(l3 => l3.OneToMany_Required3),
+ new List
+ {
+ new ExpectedInclude(
+ e => e.OneToMany_Optional1,
+ "OneToMany_Optional1"),
+ new ExpectedFilteredInclude(
+ e => e.OneToMany_Optional2,
+ "OneToMany_Optional2",
+ "OneToMany_Optional1",
+ includeFilter: x => x.Where(x => x.Name != "Foo").OrderBy(x => x.Id).Take(1)),
+ new ExpectedInclude(
+ e => e.OneToMany_Optional3,
+ "OneToMany_Optional3",
+ "OneToMany_Optional1.OneToMany_Optional2"),
+ new ExpectedInclude(
+ e => e.OneToMany_Required3,
+ "OneToMany_Required3",
+ "OneToMany_Optional1.OneToMany_Optional2"),
+ });
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Filtered_include_complex_three_level_with_middle_having_filter2(bool async)
+ {
+ return AssertIncludeQuery(
+ async,
+ ss => ss.Set()
+ .Include(l1 => l1.OneToMany_Optional1).ThenInclude(l2 => l2.OneToMany_Optional2.Where(x => x.Name != "Foo").OrderBy(x => x.Id).Take(1)).ThenInclude(l3 => l3.OneToMany_Optional3)
+ .Include(l1 => l1.OneToMany_Optional1).ThenInclude(l2 => l2.OneToMany_Optional2).ThenInclude(l3 => l3.OneToMany_Required3),
+ new List
+ {
+ new ExpectedInclude(
+ e => e.OneToMany_Optional1,
+ "OneToMany_Optional1"),
+ new ExpectedFilteredInclude(
+ e => e.OneToMany_Optional2,
+ "OneToMany_Optional2",
+ "OneToMany_Optional1",
+ includeFilter: x => x.Where(x => x.Name != "Foo").OrderBy(x => x.Id).Take(1)),
+ new ExpectedInclude(
+ e => e.OneToMany_Optional3,
+ "OneToMany_Optional3",
+ "OneToMany_Optional1.OneToMany_Optional2"),
+ new ExpectedInclude(
+ e => e.OneToMany_Required3,
+ "OneToMany_Required3",
+ "OneToMany_Optional1.OneToMany_Optional2"),
+ });
+ }
+
+ [ConditionalFact]
+ public virtual void Filtered_include_variable_used_inside_filter()
+ {
+ using var ctx = CreateContext();
+ var prm = "Foo";
+ var query = ctx.LevelOne
+ .Include(l1 => l1.OneToMany_Optional1.Where(x => x.Name != prm).OrderBy(x => x.Id).Take(3));
+ var result = query.ToList();
+ }
+
+ [ConditionalFact]
+ public virtual void Filtered_include_context_accessed_inside_filter()
+ {
+ using var ctx = CreateContext();
+ var query = ctx.LevelOne
+ .Include(l1 => l1.OneToMany_Optional1.Where(x => ctx.LevelOne.Count() > 7).OrderBy(x => x.Id).Take(3));
+ var result = query.ToList();
+ }
+
+ [ConditionalFact]
+ public virtual void Filtered_include_context_accessed_inside_filter_correlated()
+ {
+ using var ctx = CreateContext();
+ var query = ctx.LevelOne
+ .Include(l1 => l1.OneToMany_Optional1.Where(x => ctx.LevelOne.Count(xx => xx.Id != x.Id) > 1).OrderBy(x => x.Id).Take(3));
+ var result = query.ToList();
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual async Task Filtered_include_include_parameter_used_inside_filter_throws(bool async)
+ {
+ await Assert.ThrowsAsync(
+ () => AssertQuery(
+ async,
+ ss => ss.Set()
+ .Select(l1 => ss.Set().Include(l2 => l2.OneToMany_Optional2.Where(x => x.Id != l2.Id)))));
+ }
+
+ [ConditionalFact]
+ public virtual void Filtered_include_outer_parameter_used_inside_filter()
+ {
+ // TODO: needs #18191 for result verification
+ using var ctx = CreateContext();
+ var query = ctx.LevelOne.Select(l1 => new
+ {
+ l1.Id,
+ FullInclude = ctx.LevelTwo.Include(l2 => l2.OneToMany_Optional2).ToList(),
+ FilteredInclude = ctx.LevelTwo.Include(l2 => l2.OneToMany_Optional2.Where(x => x.Id != l1.Id)).ToList() });
+ var result = query.ToList();
+ }
+
+ [ConditionalFact]
+ public virtual void Filtered_include_is_considered_loaded()
+ {
+ using var ctx = CreateContext();
+ var query = ctx.LevelOne.AsTracking().Include(l1 => l1.OneToMany_Optional1.OrderBy(x => x.Id).Take(1));
+ var result = query.ToList();
+ foreach (var resultElement in result)
+ {
+ var entry = ctx.Entry(resultElement);
+ Assert.True(entry.Navigation("OneToMany_Optional1").IsLoaded);
+ }
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual async Task Filtered_include_with_Distinct_throws(bool async)
+ {
+ var message = (await Assert.ThrowsAsync(
+ () => AssertQuery(
+ async,
+ ss => ss.Set().Include(l1 => l1.OneToMany_Optional1.Distinct())))).Message;
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual async Task Filtered_include_calling_methods_directly_on_parameter_throws(bool async)
+ {
+ var message = (await Assert.ThrowsAsync(
+ () => AssertQuery(
+ async,
+ ss => ss.Set()
+ .Include(l1 => l1.OneToMany_Optional1)
+ .ThenInclude(l2 => l2.AsQueryable().Where(xx => xx.Id != 42))))).Message;
+ }
}
}
diff --git a/test/EFCore.Specification.Tests/Query/ComplexNavigationsWeakQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/ComplexNavigationsWeakQueryTestBase.cs
index 9d1212c727e..fb47a9c9812 100644
--- a/test/EFCore.Specification.Tests/Query/ComplexNavigationsWeakQueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/ComplexNavigationsWeakQueryTestBase.cs
@@ -169,5 +169,10 @@ public override Task Include_inside_subquery(bool async)
{
return base.Include_inside_subquery(async);
}
+
+ public override void Filtered_include_outer_parameter_used_inside_filter()
+ {
+ // TODO: this test can be ran with weak entities once #18191 is fixed and we can use query test infra properly
+ }
}
}
diff --git a/test/EFCore.Specification.Tests/Query/NorthwindIncludeQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindIncludeQueryTestBase.cs
index bce3a83998b..36e562d7af2 100644
--- a/test/EFCore.Specification.Tests/Query/NorthwindIncludeQueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/NorthwindIncludeQueryTestBase.cs
@@ -124,7 +124,7 @@ public virtual void Include_property_expression_invalid()
Expression.Parameter(typeof(Order), "o"));
Assert.Equal(
- CoreStrings.InvalidIncludeLambdaExpression("Include", lambdaExpression.ToString()),
+ CoreStrings.InvalidIncludeExpression(lambdaExpression.Body.ToString()),
Assert.Throws(
() =>
{
@@ -196,7 +196,7 @@ public virtual void Then_include_property_expression_invalid()
Expression.Parameter(typeof(Order), "o"));
Assert.Equal(
- CoreStrings.InvalidIncludeLambdaExpression("ThenInclude", lambdaExpression.ToString()),
+ CoreStrings.InvalidIncludeExpression(lambdaExpression.Body.ToString()),
Assert.Throws(
() =>
{
diff --git a/test/EFCore.Specification.Tests/TestUtilities/ExpectedFilteredInclude.cs b/test/EFCore.Specification.Tests/TestUtilities/ExpectedFilteredInclude.cs
new file mode 100644
index 00000000000..d00bbab0511
--- /dev/null
+++ b/test/EFCore.Specification.Tests/TestUtilities/ExpectedFilteredInclude.cs
@@ -0,0 +1,23 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.EntityFrameworkCore.TestUtilities
+{
+ public class ExpectedFilteredInclude : ExpectedInclude
+ {
+ public Func, IEnumerable> IncludeFilter { get; }
+
+ public ExpectedFilteredInclude(
+ Func> include,
+ string includedName,
+ string navigationPath = "",
+ Func, IEnumerable> includeFilter = null)
+ : base(include, includedName, navigationPath)
+ {
+ IncludeFilter = includeFilter;
+ }
+ }
+}
diff --git a/test/EFCore.Specification.Tests/TestUtilities/IncludeQueryResultAsserter.cs b/test/EFCore.Specification.Tests/TestUtilities/IncludeQueryResultAsserter.cs
index e675ae80e0a..0402786177e 100644
--- a/test/EFCore.Specification.Tests/TestUtilities/IncludeQueryResultAsserter.cs
+++ b/test/EFCore.Specification.Tests/TestUtilities/IncludeQueryResultAsserter.cs
@@ -2,7 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Collections;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using System.Reflection;
using Xunit;
@@ -15,6 +17,7 @@ public class IncludeQueryResultAsserter
private readonly MethodInfo _assertCollectionMethodInfo;
private readonly Dictionary _entitySorters;
private readonly Dictionary _entityAsserters;
+ private readonly MethodInfo _filterMethodInfo;
private List _path;
private Stack _fullPath;
@@ -28,6 +31,7 @@ public IncludeQueryResultAsserter(
_assertElementMethodInfo = typeof(IncludeQueryResultAsserter).GetTypeInfo().GetDeclaredMethod(nameof(AssertElement));
_assertCollectionMethodInfo = typeof(IncludeQueryResultAsserter).GetTypeInfo().GetDeclaredMethod(nameof(AssertCollection));
+ _filterMethodInfo = typeof(IncludeQueryResultAsserter).GetTypeInfo().GetDeclaredMethod(nameof(Filter));
}
public virtual void AssertResult(object expected, object actual, IEnumerable expectedIncludes)
@@ -149,9 +153,22 @@ protected virtual void AssertCollection(
protected void ProcessIncludes(TEntity expected, TEntity actual, IEnumerable expectedIncludes)
{
var currentPath = string.Join(".", _path);
+
foreach (var expectedInclude in expectedIncludes.OfType>().Where(i => i.NavigationPath == currentPath))
{
var expectedIncludedNavigation = expectedInclude.Include(expected);
+ if (expectedInclude.GetType().BaseType != typeof(object))
+ {
+ var includedType = expectedInclude.GetType().GetGenericArguments()[1];
+ var filterTypedMethod = _filterMethodInfo.MakeGenericMethod(typeof(TEntity), includedType);
+ expectedIncludedNavigation = filterTypedMethod.Invoke(
+ this,
+ BindingFlags.NonPublic,
+ null,
+ new object[] { expectedIncludedNavigation, expectedInclude },
+ CultureInfo.CurrentCulture);
+ }
+
var actualIncludedNavigation = expectedInclude.Include(actual);
_path.Add(expectedInclude.IncludedName);
@@ -164,6 +181,11 @@ protected void ProcessIncludes(TEntity expected, TEntity actual, IEnume
}
}
+ private IEnumerable Filter(
+ IEnumerable expected,
+ ExpectedFilteredInclude expectedFilteredInclude)
+ => expectedFilteredInclude.IncludeFilter(expected);
+
// for debugging purposes
protected string FullPath => string.Join(string.Empty, _fullPath.Reverse());
}
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs
index 570051b9f4b..01878802fda 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs
@@ -4476,6 +4476,437 @@ FROM [LevelOne] AS [l1]
LEFT JOIN [LevelTwo] AS [l3] ON [l1].[Id] = [l3].[OneToOne_Optional_PK_Inverse2Id]");
}
+ public override async Task Filtered_include_basic_Where(bool async)
+ {
+ await base.Filtered_include_basic_Where(async);
+
+ AssertSql(
+ @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id]
+FROM [LevelOne] AS [l]
+LEFT JOIN (
+ SELECT [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id]
+ FROM [LevelTwo] AS [l0]
+ WHERE [l0].[Id] > 5
+) AS [t] ON [l].[Id] = [t].[OneToMany_Optional_Inverse2Id]
+ORDER BY [l].[Id], [t].[Id]");
+ }
+
+ public override async Task Filtered_include_basic_OrderBy_Take(bool async)
+ {
+ await base.Filtered_include_basic_OrderBy_Take(async);
+
+ AssertSql(
+ @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id]
+FROM [LevelOne] AS [l]
+OUTER APPLY (
+ SELECT TOP(3) [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id]
+ FROM [LevelTwo] AS [l0]
+ WHERE [l].[Id] = [l0].[OneToMany_Optional_Inverse2Id]
+ ORDER BY [l0].[Name]
+) AS [t]
+ORDER BY [l].[Id], [t].[Name], [t].[Id]");
+ }
+
+ public override async Task Filtered_include_basic_OrderBy_Skip(bool async)
+ {
+ await base.Filtered_include_basic_OrderBy_Skip(async);
+
+ AssertSql(
+ @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id]
+FROM [LevelOne] AS [l]
+OUTER APPLY (
+ SELECT [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id]
+ FROM [LevelTwo] AS [l0]
+ WHERE [l].[Id] = [l0].[OneToMany_Optional_Inverse2Id]
+ ORDER BY [l0].[Name]
+ OFFSET 1 ROWS
+) AS [t]
+ORDER BY [l].[Id], [t].[Name], [t].[Id]");
+ }
+
+ public override async Task Filtered_include_basic_OrderBy_Skip_Take(bool async)
+ {
+ await base.Filtered_include_basic_OrderBy_Skip_Take(async);
+
+ AssertSql(
+ @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id]
+FROM [LevelOne] AS [l]
+OUTER APPLY (
+ SELECT [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id]
+ FROM [LevelTwo] AS [l0]
+ WHERE [l].[Id] = [l0].[OneToMany_Optional_Inverse2Id]
+ ORDER BY [l0].[Name]
+ OFFSET 1 ROWS FETCH NEXT 3 ROWS ONLY
+) AS [t]
+ORDER BY [l].[Id], [t].[Name], [t].[Id]");
+ }
+
+ public override void Filtered_include_Skip_without_OrderBy()
+ {
+ base.Filtered_include_Skip_without_OrderBy();
+
+ AssertSql(
+ @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id]
+FROM [LevelOne] AS [l]
+OUTER APPLY (
+ SELECT [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id]
+ FROM [LevelTwo] AS [l0]
+ WHERE [l].[Id] = [l0].[OneToMany_Optional_Inverse2Id]
+ ORDER BY (SELECT 1)
+ OFFSET 1 ROWS
+) AS [t]
+ORDER BY [l].[Id], [t].[Id]");
+ }
+
+ public override void Filtered_include_Take_without_OrderBy()
+ {
+ base.Filtered_include_Take_without_OrderBy();
+
+ AssertSql(
+ @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id]
+FROM [LevelOne] AS [l]
+OUTER APPLY (
+ SELECT [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id]
+ FROM [LevelTwo] AS [l0]
+ WHERE [l].[Id] = [l0].[OneToMany_Optional_Inverse2Id]
+ ORDER BY (SELECT 1)
+ OFFSET 1 ROWS
+) AS [t]
+ORDER BY [l].[Id], [t].[Id]");
+ }
+
+ public override async Task Filtered_include_on_ThenInclude(bool async)
+ {
+ await base.Filtered_include_on_ThenInclude(async);
+
+ AssertSql(
+ @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], [t].[Id], [t].[Level2_Optional_Id], [t].[Level2_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse3Id], [t].[OneToMany_Optional_Self_Inverse3Id], [t].[OneToMany_Required_Inverse3Id], [t].[OneToMany_Required_Self_Inverse3Id], [t].[OneToOne_Optional_PK_Inverse3Id], [t].[OneToOne_Optional_Self3Id]
+FROM [LevelOne] AS [l]
+LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id]
+OUTER APPLY (
+ SELECT [l1].[Id], [l1].[Level2_Optional_Id], [l1].[Level2_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse3Id], [l1].[OneToMany_Optional_Self_Inverse3Id], [l1].[OneToMany_Required_Inverse3Id], [l1].[OneToMany_Required_Self_Inverse3Id], [l1].[OneToOne_Optional_PK_Inverse3Id], [l1].[OneToOne_Optional_Self3Id]
+ FROM [LevelThree] AS [l1]
+ WHERE ([l0].[Id] IS NOT NULL AND ([l0].[Id] = [l1].[OneToMany_Optional_Inverse3Id])) AND (([l1].[Name] <> N'Foo') OR [l1].[Name] IS NULL)
+ ORDER BY [l1].[Name]
+ OFFSET 1 ROWS FETCH NEXT 3 ROWS ONLY
+) AS [t]
+ORDER BY [l].[Id], [t].[Name], [t].[Id]");
+ }
+
+ public override async Task Filtered_include_after_reference_navigation(bool async)
+ {
+ await base.Filtered_include_after_reference_navigation(async);
+
+ AssertSql(
+ @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], [t].[Id], [t].[Level2_Optional_Id], [t].[Level2_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse3Id], [t].[OneToMany_Optional_Self_Inverse3Id], [t].[OneToMany_Required_Inverse3Id], [t].[OneToMany_Required_Self_Inverse3Id], [t].[OneToOne_Optional_PK_Inverse3Id], [t].[OneToOne_Optional_Self3Id]
+FROM [LevelOne] AS [l]
+LEFT JOIN [LevelTwo] AS [l0] ON [l].[Id] = [l0].[Level1_Optional_Id]
+OUTER APPLY (
+ SELECT [l1].[Id], [l1].[Level2_Optional_Id], [l1].[Level2_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse3Id], [l1].[OneToMany_Optional_Self_Inverse3Id], [l1].[OneToMany_Required_Inverse3Id], [l1].[OneToMany_Required_Self_Inverse3Id], [l1].[OneToOne_Optional_PK_Inverse3Id], [l1].[OneToOne_Optional_Self3Id]
+ FROM [LevelThree] AS [l1]
+ WHERE ([l0].[Id] IS NOT NULL AND ([l0].[Id] = [l1].[OneToMany_Optional_Inverse3Id])) AND (([l1].[Name] <> N'Foo') OR [l1].[Name] IS NULL)
+ ORDER BY [l1].[Name]
+ OFFSET 1 ROWS FETCH NEXT 3 ROWS ONLY
+) AS [t]
+ORDER BY [l].[Id], [t].[Name], [t].[Id]");
+ }
+
+ public override async Task Filtered_include_after_different_filtered_include_same_level(bool async)
+ {
+ await base.Filtered_include_after_different_filtered_include_same_level(async);
+
+ AssertSql(
+ @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id], [t0].[Id], [t0].[Date], [t0].[Level1_Optional_Id], [t0].[Level1_Required_Id], [t0].[Name], [t0].[OneToMany_Optional_Inverse2Id], [t0].[OneToMany_Optional_Self_Inverse2Id], [t0].[OneToMany_Required_Inverse2Id], [t0].[OneToMany_Required_Self_Inverse2Id], [t0].[OneToOne_Optional_PK_Inverse2Id], [t0].[OneToOne_Optional_Self2Id]
+FROM [LevelOne] AS [l]
+OUTER APPLY (
+ SELECT TOP(3) [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id]
+ FROM [LevelTwo] AS [l0]
+ WHERE ([l].[Id] = [l0].[OneToMany_Optional_Inverse2Id]) AND (([l0].[Name] <> N'Foo') OR [l0].[Name] IS NULL)
+ ORDER BY [l0].[Name]
+) AS [t]
+OUTER APPLY (
+ SELECT [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id]
+ FROM [LevelTwo] AS [l1]
+ WHERE ([l].[Id] = [l1].[OneToMany_Required_Inverse2Id]) AND (([l1].[Name] <> N'Bar') OR [l1].[Name] IS NULL)
+ ORDER BY [l1].[Name] DESC
+ OFFSET 1 ROWS
+) AS [t0]
+ORDER BY [l].[Id], [t].[Name], [t].[Id], [t0].[Name] DESC, [t0].[Id]");
+ }
+
+ public override async Task Filtered_include_after_different_filtered_include_different_level(bool async)
+ {
+ await base.Filtered_include_after_different_filtered_include_different_level(async);
+
+ AssertSql(
+ @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t1].[Id], [t1].[Date], [t1].[Level1_Optional_Id], [t1].[Level1_Required_Id], [t1].[Name], [t1].[OneToMany_Optional_Inverse2Id], [t1].[OneToMany_Optional_Self_Inverse2Id], [t1].[OneToMany_Required_Inverse2Id], [t1].[OneToMany_Required_Self_Inverse2Id], [t1].[OneToOne_Optional_PK_Inverse2Id], [t1].[OneToOne_Optional_Self2Id], [t1].[Id0], [t1].[Level2_Optional_Id], [t1].[Level2_Required_Id], [t1].[Name0], [t1].[OneToMany_Optional_Inverse3Id], [t1].[OneToMany_Optional_Self_Inverse3Id], [t1].[OneToMany_Required_Inverse3Id], [t1].[OneToMany_Required_Self_Inverse3Id], [t1].[OneToOne_Optional_PK_Inverse3Id], [t1].[OneToOne_Optional_Self3Id]
+FROM [LevelOne] AS [l]
+OUTER APPLY (
+ SELECT [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id], [t0].[Id] AS [Id0], [t0].[Level2_Optional_Id], [t0].[Level2_Required_Id], [t0].[Name] AS [Name0], [t0].[OneToMany_Optional_Inverse3Id], [t0].[OneToMany_Optional_Self_Inverse3Id], [t0].[OneToMany_Required_Inverse3Id], [t0].[OneToMany_Required_Self_Inverse3Id], [t0].[OneToOne_Optional_PK_Inverse3Id], [t0].[OneToOne_Optional_Self3Id]
+ FROM (
+ SELECT TOP(3) [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id]
+ FROM [LevelTwo] AS [l0]
+ WHERE ([l].[Id] = [l0].[OneToMany_Optional_Inverse2Id]) AND (([l0].[Name] <> N'Foo') OR [l0].[Name] IS NULL)
+ ORDER BY [l0].[Name]
+ ) AS [t]
+ OUTER APPLY (
+ SELECT [l1].[Id], [l1].[Level2_Optional_Id], [l1].[Level2_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse3Id], [l1].[OneToMany_Optional_Self_Inverse3Id], [l1].[OneToMany_Required_Inverse3Id], [l1].[OneToMany_Required_Self_Inverse3Id], [l1].[OneToOne_Optional_PK_Inverse3Id], [l1].[OneToOne_Optional_Self3Id]
+ FROM [LevelThree] AS [l1]
+ WHERE ([t].[Id] = [l1].[OneToMany_Required_Inverse3Id]) AND (([l1].[Name] <> N'Bar') OR [l1].[Name] IS NULL)
+ ORDER BY [l1].[Name] DESC
+ OFFSET 1 ROWS
+ ) AS [t0]
+) AS [t1]
+ORDER BY [l].[Id], [t1].[Name], [t1].[Id], [t1].[Name0] DESC, [t1].[Id0]");
+ }
+
+ public override async Task Filtered_include_same_filter_set_on_same_navigation_twice(bool async)
+ {
+ await base.Filtered_include_same_filter_set_on_same_navigation_twice(async);
+
+ AssertSql(
+ @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id]
+FROM [LevelOne] AS [l]
+OUTER APPLY (
+ SELECT TOP(2) [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id]
+ FROM [LevelTwo] AS [l0]
+ WHERE ([l].[Id] = [l0].[OneToMany_Optional_Inverse2Id]) AND (([l0].[Name] <> N'Foo') OR [l0].[Name] IS NULL)
+ ORDER BY [l0].[Id] DESC
+) AS [t]
+ORDER BY [l].[Id], [t].[Id] DESC");
+ }
+
+ public override async Task Filtered_include_same_filter_set_on_same_navigation_twice_followed_by_ThenIncludes(bool async)
+ {
+ await base.Filtered_include_same_filter_set_on_same_navigation_twice_followed_by_ThenIncludes(async);
+
+ AssertSql(
+ @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t0].[Id], [t0].[Date], [t0].[Level1_Optional_Id], [t0].[Level1_Required_Id], [t0].[Name], [t0].[OneToMany_Optional_Inverse2Id], [t0].[OneToMany_Optional_Self_Inverse2Id], [t0].[OneToMany_Required_Inverse2Id], [t0].[OneToMany_Required_Self_Inverse2Id], [t0].[OneToOne_Optional_PK_Inverse2Id], [t0].[OneToOne_Optional_Self2Id], [t0].[Id0], [t0].[Level2_Optional_Id], [t0].[Level2_Required_Id], [t0].[Name0], [t0].[OneToMany_Optional_Inverse3Id], [t0].[OneToMany_Optional_Self_Inverse3Id], [t0].[OneToMany_Required_Inverse3Id], [t0].[OneToMany_Required_Self_Inverse3Id], [t0].[OneToOne_Optional_PK_Inverse3Id], [t0].[OneToOne_Optional_Self3Id], [t0].[Id1], [t0].[Level2_Optional_Id0], [t0].[Level2_Required_Id0], [t0].[Name1], [t0].[OneToMany_Optional_Inverse3Id0], [t0].[OneToMany_Optional_Self_Inverse3Id0], [t0].[OneToMany_Required_Inverse3Id0], [t0].[OneToMany_Required_Self_Inverse3Id0], [t0].[OneToOne_Optional_PK_Inverse3Id0], [t0].[OneToOne_Optional_Self3Id0]
+FROM [LevelOne] AS [l]
+OUTER APPLY (
+ SELECT [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id], [l1].[Id] AS [Id0], [l1].[Level2_Optional_Id], [l1].[Level2_Required_Id], [l1].[Name] AS [Name0], [l1].[OneToMany_Optional_Inverse3Id], [l1].[OneToMany_Optional_Self_Inverse3Id], [l1].[OneToMany_Required_Inverse3Id], [l1].[OneToMany_Required_Self_Inverse3Id], [l1].[OneToOne_Optional_PK_Inverse3Id], [l1].[OneToOne_Optional_Self3Id], [l2].[Id] AS [Id1], [l2].[Level2_Optional_Id] AS [Level2_Optional_Id0], [l2].[Level2_Required_Id] AS [Level2_Required_Id0], [l2].[Name] AS [Name1], [l2].[OneToMany_Optional_Inverse3Id] AS [OneToMany_Optional_Inverse3Id0], [l2].[OneToMany_Optional_Self_Inverse3Id] AS [OneToMany_Optional_Self_Inverse3Id0], [l2].[OneToMany_Required_Inverse3Id] AS [OneToMany_Required_Inverse3Id0], [l2].[OneToMany_Required_Self_Inverse3Id] AS [OneToMany_Required_Self_Inverse3Id0], [l2].[OneToOne_Optional_PK_Inverse3Id] AS [OneToOne_Optional_PK_Inverse3Id0], [l2].[OneToOne_Optional_Self3Id] AS [OneToOne_Optional_Self3Id0]
+ FROM (
+ SELECT TOP(2) [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id]
+ FROM [LevelTwo] AS [l0]
+ WHERE ([l].[Id] = [l0].[OneToMany_Optional_Inverse2Id]) AND (([l0].[Name] <> N'Foo') OR [l0].[Name] IS NULL)
+ ORDER BY [l0].[Id]
+ ) AS [t]
+ LEFT JOIN [LevelThree] AS [l1] ON [t].[Id] = [l1].[Level2_Required_Id]
+ LEFT JOIN [LevelThree] AS [l2] ON [t].[Id] = [l2].[OneToMany_Optional_Inverse3Id]
+) AS [t0]
+ORDER BY [l].[Id], [t0].[Id], [t0].[Id1]");
+ }
+
+ public override async Task Filtered_include_multiple_multi_level_includes_with_first_level_using_filter_include_on_one_of_the_chains_only(bool async)
+ {
+ await base.Filtered_include_multiple_multi_level_includes_with_first_level_using_filter_include_on_one_of_the_chains_only(async);
+
+ AssertSql(
+ @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t0].[Id], [t0].[Date], [t0].[Level1_Optional_Id], [t0].[Level1_Required_Id], [t0].[Name], [t0].[OneToMany_Optional_Inverse2Id], [t0].[OneToMany_Optional_Self_Inverse2Id], [t0].[OneToMany_Required_Inverse2Id], [t0].[OneToMany_Required_Self_Inverse2Id], [t0].[OneToOne_Optional_PK_Inverse2Id], [t0].[OneToOne_Optional_Self2Id], [t0].[Id0], [t0].[Level2_Optional_Id], [t0].[Level2_Required_Id], [t0].[Name0], [t0].[OneToMany_Optional_Inverse3Id], [t0].[OneToMany_Optional_Self_Inverse3Id], [t0].[OneToMany_Required_Inverse3Id], [t0].[OneToMany_Required_Self_Inverse3Id], [t0].[OneToOne_Optional_PK_Inverse3Id], [t0].[OneToOne_Optional_Self3Id], [t0].[Id1], [t0].[Level2_Optional_Id0], [t0].[Level2_Required_Id0], [t0].[Name1], [t0].[OneToMany_Optional_Inverse3Id0], [t0].[OneToMany_Optional_Self_Inverse3Id0], [t0].[OneToMany_Required_Inverse3Id0], [t0].[OneToMany_Required_Self_Inverse3Id0], [t0].[OneToOne_Optional_PK_Inverse3Id0], [t0].[OneToOne_Optional_Self3Id0]
+FROM [LevelOne] AS [l]
+OUTER APPLY (
+ SELECT [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id], [l1].[Id] AS [Id0], [l1].[Level2_Optional_Id], [l1].[Level2_Required_Id], [l1].[Name] AS [Name0], [l1].[OneToMany_Optional_Inverse3Id], [l1].[OneToMany_Optional_Self_Inverse3Id], [l1].[OneToMany_Required_Inverse3Id], [l1].[OneToMany_Required_Self_Inverse3Id], [l1].[OneToOne_Optional_PK_Inverse3Id], [l1].[OneToOne_Optional_Self3Id], [l2].[Id] AS [Id1], [l2].[Level2_Optional_Id] AS [Level2_Optional_Id0], [l2].[Level2_Required_Id] AS [Level2_Required_Id0], [l2].[Name] AS [Name1], [l2].[OneToMany_Optional_Inverse3Id] AS [OneToMany_Optional_Inverse3Id0], [l2].[OneToMany_Optional_Self_Inverse3Id] AS [OneToMany_Optional_Self_Inverse3Id0], [l2].[OneToMany_Required_Inverse3Id] AS [OneToMany_Required_Inverse3Id0], [l2].[OneToMany_Required_Self_Inverse3Id] AS [OneToMany_Required_Self_Inverse3Id0], [l2].[OneToOne_Optional_PK_Inverse3Id] AS [OneToOne_Optional_PK_Inverse3Id0], [l2].[OneToOne_Optional_Self3Id] AS [OneToOne_Optional_Self3Id0]
+ FROM (
+ SELECT TOP(2) [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id]
+ FROM [LevelTwo] AS [l0]
+ WHERE ([l].[Id] = [l0].[OneToMany_Optional_Inverse2Id]) AND (([l0].[Name] <> N'Foo') OR [l0].[Name] IS NULL)
+ ORDER BY [l0].[Id]
+ ) AS [t]
+ LEFT JOIN [LevelThree] AS [l1] ON [t].[Id] = [l1].[Level2_Required_Id]
+ LEFT JOIN [LevelThree] AS [l2] ON [t].[Id] = [l2].[OneToMany_Optional_Inverse3Id]
+) AS [t0]
+ORDER BY [l].[Id], [t0].[Id], [t0].[Id1]");
+ }
+
+ public override async Task Filtered_include_and_non_filtered_include_on_same_navigation1(bool async)
+ {
+ await base.Filtered_include_and_non_filtered_include_on_same_navigation1(async);
+
+ AssertSql(
+ @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id]
+FROM [LevelOne] AS [l]
+OUTER APPLY (
+ SELECT TOP(3) [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id]
+ FROM [LevelTwo] AS [l0]
+ WHERE ([l].[Id] = [l0].[OneToMany_Optional_Inverse2Id]) AND (([l0].[Name] <> N'Foo') OR [l0].[Name] IS NULL)
+ ORDER BY [l0].[Id]
+) AS [t]
+ORDER BY [l].[Id], [t].[Id]");
+ }
+
+ public override async Task Filtered_include_and_non_filtered_include_on_same_navigation2(bool async)
+ {
+ await base.Filtered_include_and_non_filtered_include_on_same_navigation2(async);
+
+ AssertSql(
+ @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id]
+FROM [LevelOne] AS [l]
+OUTER APPLY (
+ SELECT TOP(3) [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id]
+ FROM [LevelTwo] AS [l0]
+ WHERE ([l].[Id] = [l0].[OneToMany_Optional_Inverse2Id]) AND (([l0].[Name] <> N'Foo') OR [l0].[Name] IS NULL)
+ ORDER BY [l0].[Id]
+) AS [t]
+ORDER BY [l].[Id], [t].[Id]");
+ }
+
+ public override async Task Filtered_include_and_non_filtered_include_followed_by_then_include_on_same_navigation(bool async)
+ {
+ await base.Filtered_include_and_non_filtered_include_followed_by_then_include_on_same_navigation(async);
+
+ AssertSql(
+ @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t1].[Id], [t1].[Date], [t1].[Level1_Optional_Id], [t1].[Level1_Required_Id], [t1].[Name], [t1].[OneToMany_Optional_Inverse2Id], [t1].[OneToMany_Optional_Self_Inverse2Id], [t1].[OneToMany_Required_Inverse2Id], [t1].[OneToMany_Required_Self_Inverse2Id], [t1].[OneToOne_Optional_PK_Inverse2Id], [t1].[OneToOne_Optional_Self2Id], [t1].[Id0], [t1].[Level2_Optional_Id], [t1].[Level2_Required_Id], [t1].[Name0], [t1].[OneToMany_Optional_Inverse3Id], [t1].[OneToMany_Optional_Self_Inverse3Id], [t1].[OneToMany_Required_Inverse3Id], [t1].[OneToMany_Required_Self_Inverse3Id], [t1].[OneToOne_Optional_PK_Inverse3Id], [t1].[OneToOne_Optional_Self3Id], [t1].[Id1], [t1].[Level3_Optional_Id], [t1].[Level3_Required_Id], [t1].[Name1], [t1].[OneToMany_Optional_Inverse4Id], [t1].[OneToMany_Optional_Self_Inverse4Id], [t1].[OneToMany_Required_Inverse4Id], [t1].[OneToMany_Required_Self_Inverse4Id], [t1].[OneToOne_Optional_PK_Inverse4Id], [t1].[OneToOne_Optional_Self4Id]
+FROM [LevelOne] AS [l]
+OUTER APPLY (
+ SELECT [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id], [l1].[Id] AS [Id0], [l1].[Level2_Optional_Id], [l1].[Level2_Required_Id], [l1].[Name] AS [Name0], [l1].[OneToMany_Optional_Inverse3Id], [l1].[OneToMany_Optional_Self_Inverse3Id], [l1].[OneToMany_Required_Inverse3Id], [l1].[OneToMany_Required_Self_Inverse3Id], [l1].[OneToOne_Optional_PK_Inverse3Id], [l1].[OneToOne_Optional_Self3Id], [t0].[Id] AS [Id1], [t0].[Level3_Optional_Id], [t0].[Level3_Required_Id], [t0].[Name] AS [Name1], [t0].[OneToMany_Optional_Inverse4Id], [t0].[OneToMany_Optional_Self_Inverse4Id], [t0].[OneToMany_Required_Inverse4Id], [t0].[OneToMany_Required_Self_Inverse4Id], [t0].[OneToOne_Optional_PK_Inverse4Id], [t0].[OneToOne_Optional_Self4Id]
+ FROM (
+ SELECT TOP(1) [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id]
+ FROM [LevelTwo] AS [l0]
+ WHERE ([l].[Id] = [l0].[OneToMany_Optional_Inverse2Id]) AND (([l0].[Name] <> N'Foo') OR [l0].[Name] IS NULL)
+ ORDER BY [l0].[Id]
+ ) AS [t]
+ LEFT JOIN [LevelThree] AS [l1] ON [t].[Id] = [l1].[OneToOne_Optional_PK_Inverse3Id]
+ LEFT JOIN (
+ SELECT [l2].[Id], [l2].[Level3_Optional_Id], [l2].[Level3_Required_Id], [l2].[Name], [l2].[OneToMany_Optional_Inverse4Id], [l2].[OneToMany_Optional_Self_Inverse4Id], [l2].[OneToMany_Required_Inverse4Id], [l2].[OneToMany_Required_Self_Inverse4Id], [l2].[OneToOne_Optional_PK_Inverse4Id], [l2].[OneToOne_Optional_Self4Id]
+ FROM [LevelFour] AS [l2]
+ WHERE [l2].[Id] > 1
+ ) AS [t0] ON [l1].[Id] = [t0].[OneToMany_Optional_Inverse4Id]
+) AS [t1]
+ORDER BY [l].[Id], [t1].[Id], [t1].[Id1]");
+ }
+
+ public override async Task Filtered_include_complex_three_level_with_middle_having_filter1(bool async)
+ {
+ await base.Filtered_include_complex_three_level_with_middle_having_filter1(async);
+
+ AssertSql(
+ @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t1].[Id], [t1].[Date], [t1].[Level1_Optional_Id], [t1].[Level1_Required_Id], [t1].[Name], [t1].[OneToMany_Optional_Inverse2Id], [t1].[OneToMany_Optional_Self_Inverse2Id], [t1].[OneToMany_Required_Inverse2Id], [t1].[OneToMany_Required_Self_Inverse2Id], [t1].[OneToOne_Optional_PK_Inverse2Id], [t1].[OneToOne_Optional_Self2Id], [t1].[Id0], [t1].[Level2_Optional_Id], [t1].[Level2_Required_Id], [t1].[Name0], [t1].[OneToMany_Optional_Inverse3Id], [t1].[OneToMany_Optional_Self_Inverse3Id], [t1].[OneToMany_Required_Inverse3Id], [t1].[OneToMany_Required_Self_Inverse3Id], [t1].[OneToOne_Optional_PK_Inverse3Id], [t1].[OneToOne_Optional_Self3Id], [t1].[Id00], [t1].[Level3_Optional_Id], [t1].[Level3_Required_Id], [t1].[Name00], [t1].[OneToMany_Optional_Inverse4Id], [t1].[OneToMany_Optional_Self_Inverse4Id], [t1].[OneToMany_Required_Inverse4Id], [t1].[OneToMany_Required_Self_Inverse4Id], [t1].[OneToOne_Optional_PK_Inverse4Id], [t1].[OneToOne_Optional_Self4Id], [t1].[Id1], [t1].[Level3_Optional_Id0], [t1].[Level3_Required_Id0], [t1].[Name1], [t1].[OneToMany_Optional_Inverse4Id0], [t1].[OneToMany_Optional_Self_Inverse4Id0], [t1].[OneToMany_Required_Inverse4Id0], [t1].[OneToMany_Required_Self_Inverse4Id0], [t1].[OneToOne_Optional_PK_Inverse4Id0], [t1].[OneToOne_Optional_Self4Id0]
+FROM [LevelOne] AS [l]
+LEFT JOIN (
+ SELECT [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], [t0].[Id] AS [Id0], [t0].[Level2_Optional_Id], [t0].[Level2_Required_Id], [t0].[Name] AS [Name0], [t0].[OneToMany_Optional_Inverse3Id], [t0].[OneToMany_Optional_Self_Inverse3Id], [t0].[OneToMany_Required_Inverse3Id], [t0].[OneToMany_Required_Self_Inverse3Id], [t0].[OneToOne_Optional_PK_Inverse3Id], [t0].[OneToOne_Optional_Self3Id], [t0].[Id0] AS [Id00], [t0].[Level3_Optional_Id], [t0].[Level3_Required_Id], [t0].[Name0] AS [Name00], [t0].[OneToMany_Optional_Inverse4Id], [t0].[OneToMany_Optional_Self_Inverse4Id], [t0].[OneToMany_Required_Inverse4Id], [t0].[OneToMany_Required_Self_Inverse4Id], [t0].[OneToOne_Optional_PK_Inverse4Id], [t0].[OneToOne_Optional_Self4Id], [t0].[Id1], [t0].[Level3_Optional_Id0], [t0].[Level3_Required_Id0], [t0].[Name1], [t0].[OneToMany_Optional_Inverse4Id0], [t0].[OneToMany_Optional_Self_Inverse4Id0], [t0].[OneToMany_Required_Inverse4Id0], [t0].[OneToMany_Required_Self_Inverse4Id0], [t0].[OneToOne_Optional_PK_Inverse4Id0], [t0].[OneToOne_Optional_Self4Id0]
+ FROM [LevelTwo] AS [l0]
+ OUTER APPLY (
+ SELECT [t].[Id], [t].[Level2_Optional_Id], [t].[Level2_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse3Id], [t].[OneToMany_Optional_Self_Inverse3Id], [t].[OneToMany_Required_Inverse3Id], [t].[OneToMany_Required_Self_Inverse3Id], [t].[OneToOne_Optional_PK_Inverse3Id], [t].[OneToOne_Optional_Self3Id], [l2].[Id] AS [Id0], [l2].[Level3_Optional_Id], [l2].[Level3_Required_Id], [l2].[Name] AS [Name0], [l2].[OneToMany_Optional_Inverse4Id], [l2].[OneToMany_Optional_Self_Inverse4Id], [l2].[OneToMany_Required_Inverse4Id], [l2].[OneToMany_Required_Self_Inverse4Id], [l2].[OneToOne_Optional_PK_Inverse4Id], [l2].[OneToOne_Optional_Self4Id], [l3].[Id] AS [Id1], [l3].[Level3_Optional_Id] AS [Level3_Optional_Id0], [l3].[Level3_Required_Id] AS [Level3_Required_Id0], [l3].[Name] AS [Name1], [l3].[OneToMany_Optional_Inverse4Id] AS [OneToMany_Optional_Inverse4Id0], [l3].[OneToMany_Optional_Self_Inverse4Id] AS [OneToMany_Optional_Self_Inverse4Id0], [l3].[OneToMany_Required_Inverse4Id] AS [OneToMany_Required_Inverse4Id0], [l3].[OneToMany_Required_Self_Inverse4Id] AS [OneToMany_Required_Self_Inverse4Id0], [l3].[OneToOne_Optional_PK_Inverse4Id] AS [OneToOne_Optional_PK_Inverse4Id0], [l3].[OneToOne_Optional_Self4Id] AS [OneToOne_Optional_Self4Id0]
+ FROM (
+ SELECT TOP(1) [l1].[Id], [l1].[Level2_Optional_Id], [l1].[Level2_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse3Id], [l1].[OneToMany_Optional_Self_Inverse3Id], [l1].[OneToMany_Required_Inverse3Id], [l1].[OneToMany_Required_Self_Inverse3Id], [l1].[OneToOne_Optional_PK_Inverse3Id], [l1].[OneToOne_Optional_Self3Id]
+ FROM [LevelThree] AS [l1]
+ WHERE ([l0].[Id] = [l1].[OneToMany_Optional_Inverse3Id]) AND (([l1].[Name] <> N'Foo') OR [l1].[Name] IS NULL)
+ ORDER BY [l1].[Id]
+ ) AS [t]
+ LEFT JOIN [LevelFour] AS [l2] ON [t].[Id] = [l2].[OneToMany_Optional_Inverse4Id]
+ LEFT JOIN [LevelFour] AS [l3] ON [t].[Id] = [l3].[OneToMany_Required_Inverse4Id]
+ ) AS [t0]
+) AS [t1] ON [l].[Id] = [t1].[OneToMany_Optional_Inverse2Id]
+ORDER BY [l].[Id], [t1].[Id], [t1].[Id0], [t1].[Id00], [t1].[Id1]");
+ }
+
+ public override async Task Filtered_include_complex_three_level_with_middle_having_filter2(bool async)
+ {
+ await base.Filtered_include_complex_three_level_with_middle_having_filter2(async);
+
+ AssertSql(
+ @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t1].[Id], [t1].[Date], [t1].[Level1_Optional_Id], [t1].[Level1_Required_Id], [t1].[Name], [t1].[OneToMany_Optional_Inverse2Id], [t1].[OneToMany_Optional_Self_Inverse2Id], [t1].[OneToMany_Required_Inverse2Id], [t1].[OneToMany_Required_Self_Inverse2Id], [t1].[OneToOne_Optional_PK_Inverse2Id], [t1].[OneToOne_Optional_Self2Id], [t1].[Id0], [t1].[Level2_Optional_Id], [t1].[Level2_Required_Id], [t1].[Name0], [t1].[OneToMany_Optional_Inverse3Id], [t1].[OneToMany_Optional_Self_Inverse3Id], [t1].[OneToMany_Required_Inverse3Id], [t1].[OneToMany_Required_Self_Inverse3Id], [t1].[OneToOne_Optional_PK_Inverse3Id], [t1].[OneToOne_Optional_Self3Id], [t1].[Id00], [t1].[Level3_Optional_Id], [t1].[Level3_Required_Id], [t1].[Name00], [t1].[OneToMany_Optional_Inverse4Id], [t1].[OneToMany_Optional_Self_Inverse4Id], [t1].[OneToMany_Required_Inverse4Id], [t1].[OneToMany_Required_Self_Inverse4Id], [t1].[OneToOne_Optional_PK_Inverse4Id], [t1].[OneToOne_Optional_Self4Id], [t1].[Id1], [t1].[Level3_Optional_Id0], [t1].[Level3_Required_Id0], [t1].[Name1], [t1].[OneToMany_Optional_Inverse4Id0], [t1].[OneToMany_Optional_Self_Inverse4Id0], [t1].[OneToMany_Required_Inverse4Id0], [t1].[OneToMany_Required_Self_Inverse4Id0], [t1].[OneToOne_Optional_PK_Inverse4Id0], [t1].[OneToOne_Optional_Self4Id0]
+FROM [LevelOne] AS [l]
+LEFT JOIN (
+ SELECT [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], [t0].[Id] AS [Id0], [t0].[Level2_Optional_Id], [t0].[Level2_Required_Id], [t0].[Name] AS [Name0], [t0].[OneToMany_Optional_Inverse3Id], [t0].[OneToMany_Optional_Self_Inverse3Id], [t0].[OneToMany_Required_Inverse3Id], [t0].[OneToMany_Required_Self_Inverse3Id], [t0].[OneToOne_Optional_PK_Inverse3Id], [t0].[OneToOne_Optional_Self3Id], [t0].[Id0] AS [Id00], [t0].[Level3_Optional_Id], [t0].[Level3_Required_Id], [t0].[Name0] AS [Name00], [t0].[OneToMany_Optional_Inverse4Id], [t0].[OneToMany_Optional_Self_Inverse4Id], [t0].[OneToMany_Required_Inverse4Id], [t0].[OneToMany_Required_Self_Inverse4Id], [t0].[OneToOne_Optional_PK_Inverse4Id], [t0].[OneToOne_Optional_Self4Id], [t0].[Id1], [t0].[Level3_Optional_Id0], [t0].[Level3_Required_Id0], [t0].[Name1], [t0].[OneToMany_Optional_Inverse4Id0], [t0].[OneToMany_Optional_Self_Inverse4Id0], [t0].[OneToMany_Required_Inverse4Id0], [t0].[OneToMany_Required_Self_Inverse4Id0], [t0].[OneToOne_Optional_PK_Inverse4Id0], [t0].[OneToOne_Optional_Self4Id0]
+ FROM [LevelTwo] AS [l0]
+ OUTER APPLY (
+ SELECT [t].[Id], [t].[Level2_Optional_Id], [t].[Level2_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse3Id], [t].[OneToMany_Optional_Self_Inverse3Id], [t].[OneToMany_Required_Inverse3Id], [t].[OneToMany_Required_Self_Inverse3Id], [t].[OneToOne_Optional_PK_Inverse3Id], [t].[OneToOne_Optional_Self3Id], [l2].[Id] AS [Id0], [l2].[Level3_Optional_Id], [l2].[Level3_Required_Id], [l2].[Name] AS [Name0], [l2].[OneToMany_Optional_Inverse4Id], [l2].[OneToMany_Optional_Self_Inverse4Id], [l2].[OneToMany_Required_Inverse4Id], [l2].[OneToMany_Required_Self_Inverse4Id], [l2].[OneToOne_Optional_PK_Inverse4Id], [l2].[OneToOne_Optional_Self4Id], [l3].[Id] AS [Id1], [l3].[Level3_Optional_Id] AS [Level3_Optional_Id0], [l3].[Level3_Required_Id] AS [Level3_Required_Id0], [l3].[Name] AS [Name1], [l3].[OneToMany_Optional_Inverse4Id] AS [OneToMany_Optional_Inverse4Id0], [l3].[OneToMany_Optional_Self_Inverse4Id] AS [OneToMany_Optional_Self_Inverse4Id0], [l3].[OneToMany_Required_Inverse4Id] AS [OneToMany_Required_Inverse4Id0], [l3].[OneToMany_Required_Self_Inverse4Id] AS [OneToMany_Required_Self_Inverse4Id0], [l3].[OneToOne_Optional_PK_Inverse4Id] AS [OneToOne_Optional_PK_Inverse4Id0], [l3].[OneToOne_Optional_Self4Id] AS [OneToOne_Optional_Self4Id0]
+ FROM (
+ SELECT TOP(1) [l1].[Id], [l1].[Level2_Optional_Id], [l1].[Level2_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse3Id], [l1].[OneToMany_Optional_Self_Inverse3Id], [l1].[OneToMany_Required_Inverse3Id], [l1].[OneToMany_Required_Self_Inverse3Id], [l1].[OneToOne_Optional_PK_Inverse3Id], [l1].[OneToOne_Optional_Self3Id]
+ FROM [LevelThree] AS [l1]
+ WHERE ([l0].[Id] = [l1].[OneToMany_Optional_Inverse3Id]) AND (([l1].[Name] <> N'Foo') OR [l1].[Name] IS NULL)
+ ORDER BY [l1].[Id]
+ ) AS [t]
+ LEFT JOIN [LevelFour] AS [l2] ON [t].[Id] = [l2].[OneToMany_Optional_Inverse4Id]
+ LEFT JOIN [LevelFour] AS [l3] ON [t].[Id] = [l3].[OneToMany_Required_Inverse4Id]
+ ) AS [t0]
+) AS [t1] ON [l].[Id] = [t1].[OneToMany_Optional_Inverse2Id]
+ORDER BY [l].[Id], [t1].[Id], [t1].[Id0], [t1].[Id00], [t1].[Id1]");
+ }
+
+ public override void Filtered_include_variable_used_inside_filter()
+ {
+ base.Filtered_include_variable_used_inside_filter();
+
+ AssertSql(
+ @"@__prm_0='Foo' (Size = 4000)
+
+SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id]
+FROM [LevelOne] AS [l]
+OUTER APPLY (
+ SELECT TOP(3) [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id]
+ FROM [LevelTwo] AS [l0]
+ WHERE ([l].[Id] = [l0].[OneToMany_Optional_Inverse2Id]) AND (([l0].[Name] <> @__prm_0) OR [l0].[Name] IS NULL)
+ ORDER BY [l0].[Id]
+) AS [t]
+ORDER BY [l].[Id], [t].[Id]");
+ }
+
+ public override void Filtered_include_context_accessed_inside_filter()
+ {
+ base.Filtered_include_context_accessed_inside_filter();
+
+ AssertSql(
+ @"SELECT COUNT(*)
+FROM [LevelOne] AS [l]",
+ //
+ @"@__p_0='True'
+
+SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id]
+FROM [LevelOne] AS [l]
+OUTER APPLY (
+ SELECT TOP(3) [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id]
+ FROM [LevelTwo] AS [l0]
+ WHERE ([l].[Id] = [l0].[OneToMany_Optional_Inverse2Id]) AND (@__p_0 = CAST(1 AS bit))
+ ORDER BY [l0].[Id]
+) AS [t]
+ORDER BY [l].[Id], [t].[Id]");
+ }
+
+ public override void Filtered_include_context_accessed_inside_filter_correlated()
+ {
+ base.Filtered_include_context_accessed_inside_filter_correlated();
+
+ AssertSql(
+ @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_Inverse1Id], [l].[OneToMany_Required_Self_Inverse1Id], [l].[OneToOne_Optional_Self1Id], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id]
+FROM [LevelOne] AS [l]
+OUTER APPLY (
+ SELECT TOP(3) [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id]
+ FROM [LevelTwo] AS [l0]
+ WHERE ([l].[Id] = [l0].[OneToMany_Optional_Inverse2Id]) AND ((
+ SELECT COUNT(*)
+ FROM [LevelOne] AS [l1]
+ WHERE [l1].[Id] <> [l0].[Id]) > 1)
+ ORDER BY [l0].[Id]
+) AS [t]
+ORDER BY [l].[Id], [t].[Id]");
+ }
+
+ public override void Filtered_include_outer_parameter_used_inside_filter()
+ {
+ base.Filtered_include_outer_parameter_used_inside_filter();
+
+ AssertSql(
+ @"SELECT [l].[Id], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_Inverse2Id], [t].[OneToMany_Optional_Self_Inverse2Id], [t].[OneToMany_Required_Inverse2Id], [t].[OneToMany_Required_Self_Inverse2Id], [t].[OneToOne_Optional_PK_Inverse2Id], [t].[OneToOne_Optional_Self2Id], [t].[Id0], [t].[Level2_Optional_Id], [t].[Level2_Required_Id], [t].[Name0], [t].[OneToMany_Optional_Inverse3Id], [t].[OneToMany_Optional_Self_Inverse3Id], [t].[OneToMany_Required_Inverse3Id], [t].[OneToMany_Required_Self_Inverse3Id], [t].[OneToOne_Optional_PK_Inverse3Id], [t].[OneToOne_Optional_Self3Id], [t1].[Id], [t1].[Date], [t1].[Level1_Optional_Id], [t1].[Level1_Required_Id], [t1].[Name], [t1].[OneToMany_Optional_Inverse2Id], [t1].[OneToMany_Optional_Self_Inverse2Id], [t1].[OneToMany_Required_Inverse2Id], [t1].[OneToMany_Required_Self_Inverse2Id], [t1].[OneToOne_Optional_PK_Inverse2Id], [t1].[OneToOne_Optional_Self2Id], [t1].[Id0], [t1].[Level2_Optional_Id], [t1].[Level2_Required_Id], [t1].[Name0], [t1].[OneToMany_Optional_Inverse3Id], [t1].[OneToMany_Optional_Self_Inverse3Id], [t1].[OneToMany_Required_Inverse3Id], [t1].[OneToMany_Required_Self_Inverse3Id], [t1].[OneToOne_Optional_PK_Inverse3Id], [t1].[OneToOne_Optional_Self3Id]
+FROM [LevelOne] AS [l]
+OUTER APPLY (
+ SELECT [l0].[Id], [l0].[Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Optional_Self_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToMany_Required_Self_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l0].[OneToOne_Optional_Self2Id], [l1].[Id] AS [Id0], [l1].[Level2_Optional_Id], [l1].[Level2_Required_Id], [l1].[Name] AS [Name0], [l1].[OneToMany_Optional_Inverse3Id], [l1].[OneToMany_Optional_Self_Inverse3Id], [l1].[OneToMany_Required_Inverse3Id], [l1].[OneToMany_Required_Self_Inverse3Id], [l1].[OneToOne_Optional_PK_Inverse3Id], [l1].[OneToOne_Optional_Self3Id]
+ FROM [LevelTwo] AS [l0]
+ LEFT JOIN [LevelThree] AS [l1] ON [l0].[Id] = [l1].[OneToMany_Optional_Inverse3Id]
+) AS [t]
+OUTER APPLY (
+ SELECT [l2].[Id], [l2].[Date], [l2].[Level1_Optional_Id], [l2].[Level1_Required_Id], [l2].[Name], [l2].[OneToMany_Optional_Inverse2Id], [l2].[OneToMany_Optional_Self_Inverse2Id], [l2].[OneToMany_Required_Inverse2Id], [l2].[OneToMany_Required_Self_Inverse2Id], [l2].[OneToOne_Optional_PK_Inverse2Id], [l2].[OneToOne_Optional_Self2Id], [t0].[Id] AS [Id0], [t0].[Level2_Optional_Id], [t0].[Level2_Required_Id], [t0].[Name] AS [Name0], [t0].[OneToMany_Optional_Inverse3Id], [t0].[OneToMany_Optional_Self_Inverse3Id], [t0].[OneToMany_Required_Inverse3Id], [t0].[OneToMany_Required_Self_Inverse3Id], [t0].[OneToOne_Optional_PK_Inverse3Id], [t0].[OneToOne_Optional_Self3Id]
+ FROM [LevelTwo] AS [l2]
+ LEFT JOIN (
+ SELECT [l3].[Id], [l3].[Level2_Optional_Id], [l3].[Level2_Required_Id], [l3].[Name], [l3].[OneToMany_Optional_Inverse3Id], [l3].[OneToMany_Optional_Self_Inverse3Id], [l3].[OneToMany_Required_Inverse3Id], [l3].[OneToMany_Required_Self_Inverse3Id], [l3].[OneToOne_Optional_PK_Inverse3Id], [l3].[OneToOne_Optional_Self3Id]
+ FROM [LevelThree] AS [l3]
+ WHERE [l3].[Id] <> [l].[Id]
+ ) AS [t0] ON [l2].[Id] = [t0].[OneToMany_Optional_Inverse3Id]
+) AS [t1]
+ORDER BY [l].[Id], [t].[Id], [t].[Id0], [t1].[Id], [t1].[Id0]");
+ }
+
private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}
}
diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsQuerySqliteTest.cs
index f82f06b6a3c..f52478eadec 100644
--- a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsQuerySqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsQuerySqliteTest.cs
@@ -34,5 +34,27 @@ public override Task Include_inside_subquery(bool async)
// Sqlite does not support cross/outer apply
public override Task SelectMany_with_outside_reference_to_joined_table_correctly_translated_to_apply(bool async) => null;
public override Task Nested_SelectMany_correlated_with_join_table_correctly_translated_to_apply(bool async) => null;
+ public override void Filtered_include_Skip_without_OrderBy() { }
+ public override void Filtered_include_Take_without_OrderBy() { }
+ public override Task Filtered_include_after_different_filtered_include_same_level(bool async) => null;
+ public override Task Filtered_include_after_different_filtered_include_different_level(bool async) => null;
+ public override Task Filtered_include_after_reference_navigation(bool async) => null;
+ public override Task Filtered_include_and_non_filtered_include_on_same_navigation1(bool async) => null;
+ public override Task Filtered_include_and_non_filtered_include_on_same_navigation2(bool async) => null;
+ public override Task Filtered_include_basic_OrderBy_Take(bool async) => null;
+ public override Task Filtered_include_basic_OrderBy_Skip(bool async) => null;
+ public override Task Filtered_include_basic_OrderBy_Skip_Take(bool async) => null;
+ public override void Filtered_include_context_accessed_inside_filter() { }
+ public override void Filtered_include_context_accessed_inside_filter_correlated() { }
+ public override Task Filtered_include_on_ThenInclude(bool async) => null;
+ public override void Filtered_include_outer_parameter_used_inside_filter() { }
+ public override void Filtered_include_variable_used_inside_filter() { }
+ public override void Filtered_include_is_considered_loaded() { }
+ public override Task Filtered_include_and_non_filtered_include_followed_by_then_include_on_same_navigation(bool async) => null;
+ public override Task Filtered_include_complex_three_level_with_middle_having_filter2(bool async) => null;
+ public override Task Filtered_include_multiple_multi_level_includes_with_first_level_using_filter_include_on_one_of_the_chains_only(bool async) => null;
+ public override Task Filtered_include_same_filter_set_on_same_navigation_twice(bool async) => null;
+ public override Task Filtered_include_same_filter_set_on_same_navigation_twice_followed_by_ThenIncludes(bool async) => null;
+ public override Task Filtered_include_complex_three_level_with_middle_having_filter1(bool async) => null;
}
}
diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsWeakQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsWeakQuerySqliteTest.cs
index b33bff236a4..1877628df6f 100644
--- a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsWeakQuerySqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsWeakQuerySqliteTest.cs
@@ -29,5 +29,26 @@ public override Task Project_collection_navigation_nested_with_take(bool async)
// Sqlite does not support cross/outer apply
public override Task SelectMany_with_outside_reference_to_joined_table_correctly_translated_to_apply(bool async) => null;
public override Task Nested_SelectMany_correlated_with_join_table_correctly_translated_to_apply(bool async) => null;
+ public override void Filtered_include_Skip_without_OrderBy() { }
+ public override void Filtered_include_Take_without_OrderBy() { }
+ public override Task Filtered_include_after_different_filtered_include_same_level(bool async) => null;
+ public override Task Filtered_include_after_different_filtered_include_different_level(bool async) => null;
+ public override Task Filtered_include_after_reference_navigation(bool async) => null;
+ public override Task Filtered_include_and_non_filtered_include_on_same_navigation1(bool async) => null;
+ public override Task Filtered_include_and_non_filtered_include_on_same_navigation2(bool async) => null;
+ public override Task Filtered_include_basic_OrderBy_Take(bool async) => null;
+ public override Task Filtered_include_basic_OrderBy_Skip(bool async) => null;
+ public override Task Filtered_include_basic_OrderBy_Skip_Take(bool async) => null;
+ public override void Filtered_include_context_accessed_inside_filter() { }
+ public override void Filtered_include_context_accessed_inside_filter_correlated() { }
+ public override Task Filtered_include_on_ThenInclude(bool async) => null;
+ public override void Filtered_include_variable_used_inside_filter() { }
+ public override void Filtered_include_is_considered_loaded() { }
+ public override Task Filtered_include_and_non_filtered_include_followed_by_then_include_on_same_navigation(bool async) => null;
+ public override Task Filtered_include_complex_three_level_with_middle_having_filter1(bool async) => null;
+ public override Task Filtered_include_multiple_multi_level_includes_with_first_level_using_filter_include_on_one_of_the_chains_only(bool async) => null;
+ public override Task Filtered_include_same_filter_set_on_same_navigation_twice(bool async) => null;
+ public override Task Filtered_include_same_filter_set_on_same_navigation_twice_followed_by_ThenIncludes(bool async) => null;
+ public override Task Filtered_include_complex_three_level_with_middle_having_filter2(bool async) => null;
}
}