diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
index 29010cc544f..7c0e5934361 100644
--- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
@@ -395,7 +395,24 @@ private static ShapedQueryExpression CreateShapedQueryExpression(IEntityType ent
ShapedQueryExpression source,
Expression index,
bool returnDefault)
- => null;
+ {
+ var selectExpression = (SelectExpression)source.QueryExpression;
+ var translation = TranslateExpression(index);
+ if (translation == null)
+ {
+ return null;
+ }
+
+ if (selectExpression.Orderings.Count == 0)
+ {
+ _queryCompilationContext.Logger.RowLimitingOperationWithoutOrderByWarning();
+ }
+
+ selectExpression.ApplyOffset(translation);
+ selectExpression.ApplyLimit(TranslateExpression(Expression.Constant(1))!);
+
+ return source;
+ }
///
protected override ShapedQueryExpression? TranslateExcept(ShapedQueryExpression source1, ShapedQueryExpression source2)
diff --git a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs
index 64009e35060..76005334272 100644
--- a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs
@@ -35,9 +35,9 @@ public class RelationalSqlTranslatingExpressionVisitor : ExpressionVisitor
QueryableMethods.LastWithPredicate,
QueryableMethods.LastWithoutPredicate,
QueryableMethods.LastOrDefaultWithPredicate,
- QueryableMethods.LastOrDefaultWithoutPredicate
- //QueryableMethodProvider.ElementAtMethodInfo,
- //QueryableMethodProvider.ElementAtOrDefaultMethodInfo
+ QueryableMethods.LastOrDefaultWithoutPredicate,
+ QueryableMethods.ElementAt,
+ QueryableMethods.ElementAtOrDefault
};
private static readonly List PredicateAggregateMethodInfos = new()
@@ -346,13 +346,29 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
&& SingleResultMethodInfos.Contains(nonNullMethodCallExpression.Method.GetGenericMethodDefinition()))
{
var source = nonNullMethodCallExpression.Arguments[0];
- if (nonNullMethodCallExpression.Arguments.Count == 2)
+ var genericMethod = nonNullMethodCallExpression.Method.GetGenericMethodDefinition();
+ if (genericMethod == QueryableMethods.FirstWithPredicate
+ || genericMethod == QueryableMethods.FirstOrDefaultWithPredicate
+ || genericMethod == QueryableMethods.SingleWithPredicate
+ || genericMethod == QueryableMethods.SingleOrDefaultWithPredicate
+ || genericMethod == QueryableMethods.LastWithPredicate
+ || genericMethod == QueryableMethods.LastOrDefaultWithPredicate)
{
source = Expression.Call(
QueryableMethods.Where.MakeGenericMethod(source.Type.GetSequenceType()),
source,
nonNullMethodCallExpression.Arguments[1]);
}
+ else if ((genericMethod == QueryableMethods.ElementAt || genericMethod == QueryableMethods.ElementAtOrDefault)
+ && (nonNullMethodCallExpression.Arguments[1] is not ConstantExpression constantIndex
+ || constantIndex.Value is not int constantInt
+ || constantInt != 0))
+ {
+ source = Expression.Call(
+ QueryableMethods.Skip.MakeGenericMethod(source.Type.GetSequenceType()),
+ source,
+ nonNullMethodCallExpression.Arguments[1]);
+ }
var translatedSubquery = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery(source);
if (translatedSubquery != null)
diff --git a/src/EFCore/Extensions/EntityFrameworkQueryableExtensions.cs b/src/EFCore/Extensions/EntityFrameworkQueryableExtensions.cs
index 0eb2ef4abd7..6a65cac5449 100644
--- a/src/EFCore/Extensions/EntityFrameworkQueryableExtensions.cs
+++ b/src/EFCore/Extensions/EntityFrameworkQueryableExtensions.cs
@@ -275,6 +275,87 @@ public static Task LongCountAsync(
#endregion
+ #region ElementAt/ElementAtOrDefault
+
+ ///
+ /// Asynchronously returns the element at a specified index in a sequence.
+ ///
+ ///
+ ///
+ /// Multiple active operations on the same context instance are not supported. Use to ensure
+ /// that any asynchronous operations have completed before calling another method on this context.
+ /// See Avoiding DbContext threading issues for more information and examples.
+ ///
+ ///
+ /// See Querying data with EF Core for more information and examples.
+ ///
+ ///
+ /// The type of the elements of .
+ /// An to return the element from.
+ /// The zero-based index of the element to retrieve.
+ /// A to observe while waiting for the task to complete.
+ ///
+ /// A task that represents the asynchronous operation.
+ /// The task result contains the element at a specified index in a sequence.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ ///
+ /// is less than zero.
+ ///
+ ///
+ /// If the is canceled.
+ public static Task ElementAtAsync(
+ this IQueryable source,
+ int index,
+ CancellationToken cancellationToken = default)
+ {
+ Check.NotNull(index, nameof(index));
+
+ return ExecuteAsync>(
+ QueryableMethods.ElementAt, source, Expression.Constant(index), cancellationToken);
+ }
+
+ ///
+ /// Asynchronously returns the element at a specified index in a sequence, or a default value if the index is out of range.
+ ///
+ ///
+ ///
+ /// Multiple active operations on the same context instance are not supported. Use to ensure
+ /// that any asynchronous operations have completed before calling another method on this context.
+ /// See Avoiding DbContext threading issues for more information and examples.
+ ///
+ ///
+ /// See Querying data with EF Core for more information and examples.
+ ///
+ ///
+ /// The type of the elements of .
+ /// An to return the element from.
+ /// The zero-based index of the element to retrieve.
+ /// A to observe while waiting for the task to complete.
+ ///
+ /// A task that represents the asynchronous operation.
+ /// The task result contains the element at a specified index in a sequence.
+ ///
+ ///
+ /// is .
+ ///
+ /// If the is canceled.
+ public static Task ElementAtOrDefaultAsync(
+ this IQueryable source,
+ int index,
+ CancellationToken cancellationToken = default)
+ {
+ Check.NotNull(index, nameof(index));
+
+ return ExecuteAsync>(
+ QueryableMethods.ElementAtOrDefault, source, Expression.Constant(index), cancellationToken);
+ }
+
+ #endregion
+
#region First/FirstOrDefault
///
diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs
index 221d56ebb04..a09f19db5d3 100644
--- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs
+++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs
@@ -1000,10 +1000,13 @@ private sealed class ReducingExpressionVisitor : ExpressionVisitor
if (navigationExpansionExpression.CardinalityReducingGenericMethodInfo != null)
{
+ var arguments = new List { result };
+ arguments.AddRange(navigationExpansionExpression.CardinalityReducingMethodArguments.Select(x => Visit(x)));
+
result = Expression.Call(
navigationExpansionExpression.CardinalityReducingGenericMethodInfo.MakeGenericMethod(
result.Type.GetSequenceType()),
- result);
+ arguments.ToArray());
}
return result;
diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs
index 5aff0d78046..69d8229eb31 100644
--- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs
+++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs
@@ -246,6 +246,7 @@ private set
public Expression PendingSelector { get; private set; }
public MethodInfo? CardinalityReducingGenericMethodInfo { get; private set; }
+ public List CardinalityReducingMethodArguments { get; private set; } = new();
public Type SourceElementType
=> CurrentParameter.Type;
@@ -274,8 +275,11 @@ public void AppendPendingOrdering(MethodInfo orderingMethod, Expression keySelec
public void ClearPendingOrderings()
=> _pendingOrderings.Clear();
- public void ConvertToSingleResult(MethodInfo genericMethod)
- => CardinalityReducingGenericMethodInfo = genericMethod;
+ public void ConvertToSingleResult(MethodInfo genericMethod, params Expression[] arguments)
+ {
+ CardinalityReducingGenericMethodInfo = genericMethod;
+ CardinalityReducingMethodArguments.AddRange(arguments);
+ }
public override ExpressionType NodeType
=> ExpressionType.Extension;
diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs
index 5f5b87ec477..22d87b508ee 100644
--- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs
+++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs
@@ -433,6 +433,16 @@ when QueryableMethods.IsSumWithSelector(method):
methodCallExpression.Arguments[1].UnwrapLambdaFromQuote(),
methodCallExpression.Type);
+ case nameof(Queryable.ElementAt)
+ when genericMethod == QueryableMethods.ElementAt:
+ case nameof(Queryable.ElementAtOrDefault)
+ when genericMethod == QueryableMethods.ElementAtOrDefault:
+ return ProcessElementAt(
+ source,
+ genericMethod,
+ methodCallExpression.Arguments[1],
+ methodCallExpression.Type);
+
case nameof(Queryable.Join)
when genericMethod == QueryableMethods.Join:
{
@@ -982,6 +992,22 @@ private NavigationExpansionExpression ProcessFirstSingleLastOrDefault(
return source;
}
+ private NavigationExpansionExpression ProcessElementAt(
+ NavigationExpansionExpression source,
+ MethodInfo genericMethod,
+ Expression index,
+ Type returnType)
+ {
+ if (source.PendingSelector.Type != returnType)
+ {
+ source.ApplySelector(Expression.Convert(source.PendingSelector, returnType));
+ }
+
+ source.ConvertToSingleResult(genericMethod, index);
+
+ return source;
+ }
+
// This returns Expression since it can also return a deferred GroupBy operation
private Expression ProcessGroupBy(
NavigationExpansionExpression source,
diff --git a/src/EFCore/Query/Internal/SubqueryMemberPushdownExpressionVisitor.cs b/src/EFCore/Query/Internal/SubqueryMemberPushdownExpressionVisitor.cs
index fd04ec11ded..0f3c92d67c6 100644
--- a/src/EFCore/Query/Internal/SubqueryMemberPushdownExpressionVisitor.cs
+++ b/src/EFCore/Query/Internal/SubqueryMemberPushdownExpressionVisitor.cs
@@ -24,9 +24,9 @@ public class SubqueryMemberPushdownExpressionVisitor : ExpressionVisitor
QueryableMethods.LastWithPredicate,
QueryableMethods.LastWithoutPredicate,
QueryableMethods.LastOrDefaultWithPredicate,
- QueryableMethods.LastOrDefaultWithoutPredicate
- //QueryableMethodProvider.ElementAtMethodInfo,
- //QueryableMethodProvider.ElementAtOrDefaultMethodInfo
+ QueryableMethods.LastOrDefaultWithoutPredicate,
+ QueryableMethods.ElementAt,
+ QueryableMethods.ElementAtOrDefault
};
private static readonly IDictionary PredicateLessMethodInfo = new Dictionary
@@ -163,7 +163,13 @@ private Expression PushdownMember(
var source = methodCallExpression.Arguments[0];
var queryableType = source.Type.GetSequenceType();
var genericMethod = methodCallExpression.Method.GetGenericMethodDefinition();
- if (methodCallExpression.Arguments.Count == 2)
+
+ if (genericMethod == QueryableMethods.FirstWithPredicate
+ || genericMethod == QueryableMethods.FirstOrDefaultWithPredicate
+ || genericMethod == QueryableMethods.SingleWithPredicate
+ || genericMethod == QueryableMethods.SingleOrDefaultWithPredicate
+ || genericMethod == QueryableMethods.LastWithPredicate
+ || genericMethod == QueryableMethods.LastOrDefaultWithPredicate)
{
// Move predicate to Where so that we can change shape before operator
source = Expression.Call(
@@ -203,7 +209,16 @@ private Expression PushdownMember(
Expression.Quote(Expression.Lambda(memberAccessExpression, parameter)));
}
- source = Expression.Call(genericMethod.MakeGenericMethod(source.Type.GetSequenceType()), source);
+ if (genericMethod == QueryableMethods.ElementAt
+ || genericMethod == QueryableMethods.ElementAtOrDefault)
+ {
+ var index = Visit(methodCallExpression.Arguments[1]);
+ source = Expression.Call(genericMethod.MakeGenericMethod(source.Type.GetSequenceType()), source, index);
+ }
+ else
+ {
+ source = Expression.Call(genericMethod.MakeGenericMethod(source.Type.GetSequenceType()), source);
+ }
return source.Type != returnType
? Expression.Convert(source, returnType)
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs
index 97c83867b6b..28602b874fb 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs
@@ -4584,6 +4584,27 @@ public override async Task Select_subquery_recursive_trivial_returning_queryable
AssertSql();
}
+ public override async Task Collection_navigation_equal_to_null_for_subquery_using_ElementAtOrDefault_constant_zero(bool async)
+ {
+ await AssertTranslationFailed(() => base.Collection_navigation_equal_to_null_for_subquery_using_ElementAtOrDefault_constant_zero(async));
+
+ AssertSql();
+ }
+
+ public override async Task Collection_navigation_equal_to_null_for_subquery_using_ElementAtOrDefault_constant_one(bool async)
+ {
+ await AssertTranslationFailed(() => base.Collection_navigation_equal_to_null_for_subquery_using_ElementAtOrDefault_constant_one(async));
+
+ AssertSql();
+ }
+
+ public override async Task Collection_navigation_equal_to_null_for_subquery_using_ElementAtOrDefault_parameter(bool async)
+ {
+ await AssertTranslationFailed(() => base.Collection_navigation_equal_to_null_for_subquery_using_ElementAtOrDefault_parameter(async));
+
+ AssertSql();
+ }
+
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs
index cdf0e2f8070..cea41b36843 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs
@@ -2413,6 +2413,22 @@ public override async Task First_over_custom_projection_compared_to_not_null(boo
AssertSql();
}
+ public override async Task ElementAt_over_custom_projection_compared_to_not_null(bool async)
+ {
+ // Cosmos client evaluation. Issue #17246.
+ await AssertTranslationFailed(() => base.ElementAt_over_custom_projection_compared_to_not_null(async));
+
+ AssertSql();
+ }
+
+ public override async Task ElementAtOrDefault_over_custom_projection_compared_to_null(bool async)
+ {
+ // Cosmos client evaluation. Issue #17246.
+ await AssertTranslationFailed(() => base.ElementAtOrDefault_over_custom_projection_compared_to_null(async));
+
+ AssertSql();
+ }
+
public override async Task Single_over_custom_projection_compared_to_null(bool async)
{
// Cosmos client evaluation. Issue #17246.
diff --git a/test/EFCore.InMemory.FunctionalTests/Query/GearsOfWarQueryInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/GearsOfWarQueryInMemoryTest.cs
index 049f36e53e2..ce9292645ed 100644
--- a/test/EFCore.InMemory.FunctionalTests/Query/GearsOfWarQueryInMemoryTest.cs
+++ b/test/EFCore.InMemory.FunctionalTests/Query/GearsOfWarQueryInMemoryTest.cs
@@ -94,4 +94,19 @@ public override Task Null_semantics_is_correctly_applied_for_function_comparison
// Null protection. Issue #13721.
=> Assert.ThrowsAsync(
() => base.Null_semantics_is_correctly_applied_for_function_comparisons_that_take_arguments_from_optional_navigation(async));
+
+ public override Task ElementAt_basic_with_OrderBy(bool async)
+ => Task.CompletedTask;
+
+ public override Task ElementAtOrDefault_basic_with_OrderBy(bool async)
+ => Task.CompletedTask;
+
+ public override Task ElementAtOrDefault_basic_with_OrderBy_parameter(bool async)
+ => Task.CompletedTask;
+
+ public override Task Where_subquery_with_ElementAtOrDefault_equality_to_null_with_composite_key(bool async)
+ => Task.CompletedTask;
+
+ public override Task Where_subquery_with_ElementAt_using_column_as_index(bool async)
+ => Task.CompletedTask;
}
diff --git a/test/EFCore.InMemory.FunctionalTests/Query/NorthwindMiscellaneousQueryInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/NorthwindMiscellaneousQueryInMemoryTest.cs
index 348d88fd223..80923726e61 100644
--- a/test/EFCore.InMemory.FunctionalTests/Query/NorthwindMiscellaneousQueryInMemoryTest.cs
+++ b/test/EFCore.InMemory.FunctionalTests/Query/NorthwindMiscellaneousQueryInMemoryTest.cs
@@ -56,4 +56,13 @@ public override async Task Entity_equality_through_subquery_composite_key(bool a
CoreStrings.EntityEqualityOnCompositeKeyEntitySubqueryNotSupported("==", nameof(OrderDetail)),
(await Assert.ThrowsAsync(
() => base.Entity_equality_through_subquery_composite_key(async))).Message);
+
+ public override Task Collection_navigation_equal_to_null_for_subquery_using_ElementAtOrDefault_constant_zero(bool async)
+ => Task.CompletedTask;
+
+ public override Task Collection_navigation_equal_to_null_for_subquery_using_ElementAtOrDefault_constant_one(bool async)
+ => Task.CompletedTask;
+
+ public override Task Collection_navigation_equal_to_null_for_subquery_using_ElementAtOrDefault_parameter(bool async)
+ => Task.CompletedTask;
}
diff --git a/test/EFCore.InMemory.FunctionalTests/Query/NorthwindWhereQueryInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/NorthwindWhereQueryInMemoryTest.cs
index 34689d61840..8bd9ff38ab5 100644
--- a/test/EFCore.InMemory.FunctionalTests/Query/NorthwindWhereQueryInMemoryTest.cs
+++ b/test/EFCore.InMemory.FunctionalTests/Query/NorthwindWhereQueryInMemoryTest.cs
@@ -29,4 +29,10 @@ public override async Task Where_simple_closure(bool async)
public override Task Like_with_non_string_column_using_double_cast(bool async)
// Casting int to object to string is invalid for InMemory
=> Assert.ThrowsAsync(() => base.Like_with_non_string_column_using_double_cast(async));
+
+ public override Task ElementAt_over_custom_projection_compared_to_not_null(bool async)
+ => Task.CompletedTask;
+
+ public override Task ElementAtOrDefault_over_custom_projection_compared_to_null(bool async)
+ => Task.CompletedTask;
}
diff --git a/test/EFCore.Relational.Specification.Tests/Query/ComplexNavigationsCollectionsSplitQueryRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/ComplexNavigationsCollectionsSplitQueryRelationalTestBase.cs
index c6bc0acfe1e..cc99ad178e8 100644
--- a/test/EFCore.Relational.Specification.Tests/Query/ComplexNavigationsCollectionsSplitQueryRelationalTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/Query/ComplexNavigationsCollectionsSplitQueryRelationalTestBase.cs
@@ -13,7 +13,11 @@ protected ComplexNavigationsCollectionsSplitQueryRelationalTestBase(TFixture fix
}
protected override Expression RewriteServerQueryExpression(Expression serverQueryExpression)
- => new SplitQueryRewritingExpressionVisitor().Visit(serverQueryExpression);
+ {
+ serverQueryExpression = base.RewriteServerQueryExpression(serverQueryExpression);
+
+ return new SplitQueryRewritingExpressionVisitor().Visit(serverQueryExpression);
+ }
private class SplitQueryRewritingExpressionVisitor : ExpressionVisitor
{
diff --git a/test/EFCore.Relational.Specification.Tests/Query/ComplexNavigationsCollectionsSplitSharedTypeQueryRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/ComplexNavigationsCollectionsSplitSharedTypeQueryRelationalTestBase.cs
index 2c52c8a19df..8c83897aba1 100644
--- a/test/EFCore.Relational.Specification.Tests/Query/ComplexNavigationsCollectionsSplitSharedTypeQueryRelationalTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/Query/ComplexNavigationsCollectionsSplitSharedTypeQueryRelationalTestBase.cs
@@ -20,7 +20,11 @@ public override async Task SelectMany_with_navigation_and_Distinct_projecting_co
() => base.SelectMany_with_navigation_and_Distinct_projecting_columns_including_join_key(async))).Message);
protected override Expression RewriteServerQueryExpression(Expression serverQueryExpression)
- => new SplitQueryRewritingExpressionVisitor().Visit(serverQueryExpression);
+ {
+ serverQueryExpression = base.RewriteServerQueryExpression(serverQueryExpression);
+
+ return new SplitQueryRewritingExpressionVisitor().Visit(serverQueryExpression);
+ }
private class SplitQueryRewritingExpressionVisitor : ExpressionVisitor
{
diff --git a/test/EFCore.Relational.Specification.Tests/Query/CompositeKeysSplitQueryRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/CompositeKeysSplitQueryRelationalTestBase.cs
index dfb209aa3aa..3ccbba45a7d 100644
--- a/test/EFCore.Relational.Specification.Tests/Query/CompositeKeysSplitQueryRelationalTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/Query/CompositeKeysSplitQueryRelationalTestBase.cs
@@ -12,7 +12,11 @@ public CompositeKeysSplitQueryRelationalTestBase(TFixture fixture)
}
protected override Expression RewriteServerQueryExpression(Expression serverQueryExpression)
- => new SplitQueryRewritingExpressionVisitor().Visit(serverQueryExpression);
+ {
+ serverQueryExpression = base.RewriteServerQueryExpression(serverQueryExpression);
+
+ return new SplitQueryRewritingExpressionVisitor().Visit(serverQueryExpression);
+ }
private class SplitQueryRewritingExpressionVisitor : ExpressionVisitor
{
diff --git a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs
index 44c10730303..adb2a61365c 100644
--- a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs
@@ -8181,6 +8181,49 @@ public virtual Task Where_subquery_equality_to_null_without_composite_key(bool a
async,
ss => ss.Set().Where(s => s.Weapons.OrderBy(e => e.Name).FirstOrDefault() == null));
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task ElementAt_basic_with_OrderBy(bool async)
+ => AssertElementAt(
+ async,
+ ss => ss.Set().OrderBy(g => g.FullName),
+ () => 0);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task ElementAtOrDefault_basic_with_OrderBy(bool async)
+ => AssertElementAtOrDefault(
+ async,
+ ss => ss.Set().OrderBy(g => g.FullName),
+ () => 1);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task ElementAtOrDefault_basic_with_OrderBy_parameter(bool async)
+ {
+ var prm = 2;
+
+ return AssertElementAtOrDefault(
+ async,
+ ss => ss.Set().OrderBy(g => g.FullName),
+ () => prm);
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Where_subquery_with_ElementAtOrDefault_equality_to_null_with_composite_key(bool async)
+ => AssertQuery(
+ async,
+ ss => ss.Set().Where(s => s.Members.OrderBy(e => e.Nickname).ElementAtOrDefault(2) == null));
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Where_subquery_with_ElementAt_using_column_as_index(bool async)
+ => AssertQuery(
+ async,
+ ss => ss.Set().Where(s => s.Members.OrderBy(m => m.Nickname).ElementAt(s.Id).Nickname == "Cole Train"),
+ ss => ss.Set().Where(s => s.Members.OrderBy(m => m.Nickname).ElementAtOrDefault(s.Id).Nickname == "Cole Train"));
+
protected GearsOfWarContext CreateContext()
=> Fixture.CreateContext();
diff --git a/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs
index dc6b8326cf7..81bf6547724 100644
--- a/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs
@@ -5781,4 +5781,36 @@ public virtual Task Collection_projection_after_DefaultIfEmpty(bool async)
assertOrder: true,
elementAsserter: (e, a) => AssertCollection(e.Orders, a.Orders),
entryCount: 14);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Collection_navigation_equal_to_null_for_subquery_using_ElementAtOrDefault_constant_zero(bool async)
+ => AssertQuery(
+ async,
+ ss => ss.Set().Where(c => c.Orders.OrderBy(o => o.OrderID).ElementAtOrDefault(0).OrderDetails == null),
+ ss => ss.Set().Where(c => c.Orders.OrderBy(o => o.OrderID).ElementAtOrDefault(0) == null),
+ entryCount: 2);
+
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Collection_navigation_equal_to_null_for_subquery_using_ElementAtOrDefault_constant_one(bool async)
+ => AssertQuery(
+ async,
+ ss => ss.Set().Where(c => c.Orders.OrderBy(o => o.OrderID).ElementAtOrDefault(1).OrderDetails == null),
+ ss => ss.Set().Where(c => c.Orders.OrderBy(o => o.OrderID).ElementAtOrDefault(1) == null),
+ entryCount: 3);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Collection_navigation_equal_to_null_for_subquery_using_ElementAtOrDefault_parameter(bool async)
+ {
+ var prm = 2;
+
+ return AssertQuery(
+ async,
+ ss => ss.Set().Where(c => c.Orders.OrderBy(o => o.OrderID).ElementAtOrDefault(prm).OrderDetails == null),
+ ss => ss.Set().Where(c => c.Orders.OrderBy(o => o.OrderID).ElementAtOrDefault(prm) == null),
+ entryCount: 5);
+ }
}
diff --git a/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs
index 0ab7ee3cc27..1c2c568ccce 100644
--- a/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs
@@ -2326,6 +2326,24 @@ public virtual Task First_over_custom_projection_compared_to_not_null(bool async
ss => ss.Set().Where(c => c.Orders.Select(o => new { o.OrderID }).FirstOrDefault() != null),
entryCount: 89);
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task ElementAt_over_custom_projection_compared_to_not_null(bool async)
+ => AssertQuery(
+ async,
+ ss => ss.Set().Where(c => c.Orders.Select(o => new { o.OrderID }).ElementAt(3) != null),
+ ss => ss.Set().Where(c => c.Orders.Select(o => new { o.OrderID }).ElementAtOrDefault(3) != null),
+ entryCount: 79);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task ElementAtOrDefault_over_custom_projection_compared_to_null(bool async)
+ => AssertQuery(
+ async,
+ ss => ss.Set().Where(c => c.Orders.Select(o => new { o.OrderID }).ElementAtOrDefault(7) == null),
+ ss => ss.Set().Where(c => c.Orders.Select(o => new { o.OrderID }).ElementAtOrDefault(7) == null),
+ entryCount: 43);
+
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Single_over_custom_projection_compared_to_null(bool async)
diff --git a/test/EFCore.Specification.Tests/Query/QueryTestBase.cs b/test/EFCore.Specification.Tests/Query/QueryTestBase.cs
index fa14b3dccd4..45fc352f56a 100644
--- a/test/EFCore.Specification.Tests/Query/QueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/QueryTestBase.cs
@@ -155,6 +155,44 @@ protected Task AssertAll(
=> QueryAsserter.AssertAll(
actualQuery, expectedQuery, actualPredicate, expectedPredicate, async);
+ protected Task AssertElementAt(
+ bool async,
+ Func> query,
+ Func index,
+ Action asserter = null,
+ int entryCount = 0)
+ => AssertElementAt(async, query, query, index, index, asserter, entryCount);
+
+ protected Task AssertElementAt(
+ bool async,
+ Func> actualQuery,
+ Func> expectedQuery,
+ Func actualIndex,
+ Func expectedIndex,
+ Action asserter = null,
+ int entryCount = 0)
+ => QueryAsserter.AssertElementAt(
+ actualQuery, expectedQuery, actualIndex, expectedIndex, asserter, entryCount, async);
+
+ protected Task AssertElementAtOrDefault(
+ bool async,
+ Func> query,
+ Func index,
+ Action asserter = null,
+ int entryCount = 0)
+ => AssertElementAtOrDefault(async, query, query, index, index, asserter, entryCount);
+
+ protected Task AssertElementAtOrDefault(
+ bool async,
+ Func> actualQuery,
+ Func> expectedQuery,
+ Func actualIndex,
+ Func expectedIndex,
+ Action asserter = null,
+ int entryCount = 0)
+ => QueryAsserter.AssertElementAtOrDefault(
+ actualQuery, expectedQuery, actualIndex, expectedIndex, asserter, entryCount, async);
+
protected Task AssertFirst(
bool async,
Func> query,
diff --git a/test/EFCore.Specification.Tests/TestUtilities/QueryAsserter.cs b/test/EFCore.Specification.Tests/TestUtilities/QueryAsserter.cs
index 6db110e96b2..12ac2a45e75 100644
--- a/test/EFCore.Specification.Tests/TestUtilities/QueryAsserter.cs
+++ b/test/EFCore.Specification.Tests/TestUtilities/QueryAsserter.cs
@@ -286,6 +286,50 @@ public async Task AssertAll(
Assert.Equal(expected, actual);
}
+ public async Task AssertElementAt(
+ Func> actualQuery,
+ Func> expectedQuery,
+ Func actualIndex,
+ Func expectedIndex,
+ Action asserter = null,
+ int entryCount = 0,
+ bool async = false,
+ bool filteredQuery = false)
+ {
+ using var context = _contextCreator();
+ var actual = async
+ ? await RewriteServerQuery(actualQuery(SetSourceCreator(context))).ElementAtAsync(actualIndex())
+ : RewriteServerQuery(actualQuery(SetSourceCreator(context))).ElementAt(actualIndex());
+
+ var expectedData = GetExpectedData(context, filteredQuery);
+ var expected = RewriteExpectedQuery(expectedQuery(expectedData)).ElementAt(expectedIndex());
+
+ AssertEqual(expected, actual, asserter);
+ AssertEntryCount(context, entryCount);
+ }
+
+ public async Task AssertElementAtOrDefault(
+ Func> actualQuery,
+ Func> expectedQuery,
+ Func actualIndex,
+ Func expectedIndex,
+ Action asserter = null,
+ int entryCount = 0,
+ bool async = false,
+ bool filteredQuery = false)
+ {
+ using var context = _contextCreator();
+ var actual = async
+ ? await RewriteServerQuery(actualQuery(SetSourceCreator(context))).ElementAtOrDefaultAsync(actualIndex())
+ : RewriteServerQuery(actualQuery(SetSourceCreator(context))).ElementAtOrDefault(actualIndex());
+
+ var expectedData = GetExpectedData(context, filteredQuery);
+ var expected = RewriteExpectedQuery(expectedQuery(expectedData)).ElementAtOrDefault(expectedIndex());
+
+ AssertEqual(expected, actual, asserter);
+ AssertEntryCount(context, entryCount);
+ }
+
public async Task AssertFirst(
Func> actualQuery,
Func> expectedQuery,
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs
index 3d1bb510959..be21bb90e03 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs
@@ -9949,6 +9949,86 @@ FROM [Gears] AS [g]
""");
}
+ public override async Task ElementAt_basic_with_OrderBy(bool async)
+ {
+ await base.ElementAt_basic_with_OrderBy(async);
+
+ AssertSql(
+"""
+@__p_0='0'
+
+SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank]
+FROM [Gears] AS [g]
+ORDER BY [g].[FullName]
+OFFSET @__p_0 ROWS FETCH NEXT 1 ROWS ONLY
+""");
+ }
+
+ public override async Task ElementAtOrDefault_basic_with_OrderBy(bool async)
+ {
+ await base.ElementAtOrDefault_basic_with_OrderBy(async);
+
+ AssertSql(
+"""
+@__p_0='1'
+
+SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank]
+FROM [Gears] AS [g]
+ORDER BY [g].[FullName]
+OFFSET @__p_0 ROWS FETCH NEXT 1 ROWS ONLY
+""");
+ }
+
+ public override async Task ElementAtOrDefault_basic_with_OrderBy_parameter(bool async)
+ {
+ await base.ElementAtOrDefault_basic_with_OrderBy_parameter(async);
+
+ AssertSql(
+"""
+@__p_0='2'
+
+SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank]
+FROM [Gears] AS [g]
+ORDER BY [g].[FullName]
+OFFSET @__p_0 ROWS FETCH NEXT 1 ROWS ONLY
+""");
+ }
+
+
+ public override async Task Where_subquery_with_ElementAtOrDefault_equality_to_null_with_composite_key(bool async)
+ {
+ await base.Where_subquery_with_ElementAtOrDefault_equality_to_null_with_composite_key(async);
+
+ AssertSql(
+"""
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE NOT (EXISTS (
+ SELECT 1
+ FROM [Gears] AS [g]
+ WHERE [s].[Id] = [g].[SquadId]
+ ORDER BY [g].[Nickname]
+ OFFSET 2 ROWS))
+""");
+ }
+
+ public override async Task Where_subquery_with_ElementAt_using_column_as_index(bool async)
+ {
+ await base.Where_subquery_with_ElementAt_using_column_as_index(async);
+
+ AssertSql(
+"""
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE (
+ SELECT [g].[Nickname]
+ FROM [Gears] AS [g]
+ WHERE [s].[Id] = [g].[SquadId]
+ ORDER BY [g].[Nickname]
+ OFFSET [s].[Id] ROWS FETCH NEXT 1 ROWS ONLY) = N'Cole Train'
+""");
+ }
+
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs
index cec5b7b9278..e271f27eb7e 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs
@@ -7174,6 +7174,57 @@ public override async Task EF_Property_include_on_incorrect_property_throws(bool
AssertSql();
}
+ public override async Task Collection_navigation_equal_to_null_for_subquery_using_ElementAtOrDefault_constant_zero(bool async)
+ {
+ await base.Collection_navigation_equal_to_null_for_subquery_using_ElementAtOrDefault_constant_zero(async);
+
+ AssertSql(
+"""
+SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
+FROM [Customers] AS [c]
+WHERE NOT (EXISTS (
+ SELECT 1
+ FROM [Orders] AS [o]
+ WHERE [c].[CustomerID] = [o].[CustomerID]))
+""");
+ }
+
+ public override async Task Collection_navigation_equal_to_null_for_subquery_using_ElementAtOrDefault_constant_one(bool async)
+ {
+ await base.Collection_navigation_equal_to_null_for_subquery_using_ElementAtOrDefault_constant_one(async);
+
+ AssertSql(
+"""
+SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
+FROM [Customers] AS [c]
+WHERE NOT (EXISTS (
+ SELECT 1
+ FROM [Orders] AS [o]
+ WHERE [c].[CustomerID] = [o].[CustomerID]
+ ORDER BY [o].[OrderID]
+ OFFSET 1 ROWS))
+""");
+ }
+
+ public override async Task Collection_navigation_equal_to_null_for_subquery_using_ElementAtOrDefault_parameter(bool async)
+ {
+ await base.Collection_navigation_equal_to_null_for_subquery_using_ElementAtOrDefault_parameter(async);
+
+ AssertSql(
+"""
+@__prm_0='2'
+
+SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
+FROM [Customers] AS [c]
+WHERE NOT (EXISTS (
+ SELECT 1
+ FROM [Orders] AS [o]
+ WHERE [c].[CustomerID] = [o].[CustomerID]
+ ORDER BY [o].[OrderID]
+ OFFSET @__prm_0 ROWS))
+""");
+ }
+
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs
index 8482cf83ef2..e2aeed1450f 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs
@@ -2799,6 +2799,40 @@ FROM [Orders] AS [o]
""");
}
+ public override async Task ElementAt_over_custom_projection_compared_to_not_null(bool async)
+ {
+ await base.ElementAt_over_custom_projection_compared_to_not_null(async);
+
+ AssertSql(
+"""
+SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
+FROM [Customers] AS [c]
+WHERE EXISTS (
+ SELECT 1
+ FROM [Orders] AS [o]
+ WHERE [c].[CustomerID] = [o].[CustomerID]
+ ORDER BY (SELECT 1)
+ OFFSET 3 ROWS)
+""");
+ }
+
+ public override async Task ElementAtOrDefault_over_custom_projection_compared_to_null(bool async)
+ {
+ await base.ElementAtOrDefault_over_custom_projection_compared_to_null(async);
+
+ AssertSql(
+"""
+SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
+FROM [Customers] AS [c]
+WHERE NOT (EXISTS (
+ SELECT 1
+ FROM [Orders] AS [o]
+ WHERE [c].[CustomerID] = [o].[CustomerID]
+ ORDER BY (SELECT 1)
+ OFFSET 7 ROWS))
+""");
+ }
+
public override async Task Single_over_custom_projection_compared_to_null(bool async)
{
await base.Single_over_custom_projection_compared_to_null(async);
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs
index 12020fe5257..4cc471da4de 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs
@@ -13014,6 +13014,115 @@ FROM [Officers] AS [o0]
""");
}
+ public override async Task ElementAt_basic_with_OrderBy(bool async)
+ {
+ await base.ElementAt_basic_with_OrderBy(async);
+
+ AssertSql(
+"""
+@__p_0='0'
+
+SELECT [t].[Nickname], [t].[SquadId], [t].[AssignedCityName], [t].[CityOfBirthName], [t].[FullName], [t].[HasSoulPatch], [t].[LeaderNickname], [t].[LeaderSquadId], [t].[Rank], [t].[Discriminator]
+FROM (
+ SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], N'Gear' AS [Discriminator]
+ FROM [Gears] AS [g]
+ UNION ALL
+ SELECT [o].[Nickname], [o].[SquadId], [o].[AssignedCityName], [o].[CityOfBirthName], [o].[FullName], [o].[HasSoulPatch], [o].[LeaderNickname], [o].[LeaderSquadId], [o].[Rank], N'Officer' AS [Discriminator]
+ FROM [Officers] AS [o]
+) AS [t]
+ORDER BY [t].[FullName]
+OFFSET @__p_0 ROWS FETCH NEXT 1 ROWS ONLY
+""");
+ }
+
+ public override async Task ElementAtOrDefault_basic_with_OrderBy(bool async)
+ {
+ await base.ElementAtOrDefault_basic_with_OrderBy(async);
+
+ AssertSql(
+"""
+@__p_0='1'
+
+SELECT [t].[Nickname], [t].[SquadId], [t].[AssignedCityName], [t].[CityOfBirthName], [t].[FullName], [t].[HasSoulPatch], [t].[LeaderNickname], [t].[LeaderSquadId], [t].[Rank], [t].[Discriminator]
+FROM (
+ SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], N'Gear' AS [Discriminator]
+ FROM [Gears] AS [g]
+ UNION ALL
+ SELECT [o].[Nickname], [o].[SquadId], [o].[AssignedCityName], [o].[CityOfBirthName], [o].[FullName], [o].[HasSoulPatch], [o].[LeaderNickname], [o].[LeaderSquadId], [o].[Rank], N'Officer' AS [Discriminator]
+ FROM [Officers] AS [o]
+) AS [t]
+ORDER BY [t].[FullName]
+OFFSET @__p_0 ROWS FETCH NEXT 1 ROWS ONLY
+""");
+ }
+
+ public override async Task ElementAtOrDefault_basic_with_OrderBy_parameter(bool async)
+ {
+ await base.ElementAtOrDefault_basic_with_OrderBy_parameter(async);
+
+ AssertSql(
+"""
+@__p_0='2'
+
+SELECT [t].[Nickname], [t].[SquadId], [t].[AssignedCityName], [t].[CityOfBirthName], [t].[FullName], [t].[HasSoulPatch], [t].[LeaderNickname], [t].[LeaderSquadId], [t].[Rank], [t].[Discriminator]
+FROM (
+ SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], N'Gear' AS [Discriminator]
+ FROM [Gears] AS [g]
+ UNION ALL
+ SELECT [o].[Nickname], [o].[SquadId], [o].[AssignedCityName], [o].[CityOfBirthName], [o].[FullName], [o].[HasSoulPatch], [o].[LeaderNickname], [o].[LeaderSquadId], [o].[Rank], N'Officer' AS [Discriminator]
+ FROM [Officers] AS [o]
+) AS [t]
+ORDER BY [t].[FullName]
+OFFSET @__p_0 ROWS FETCH NEXT 1 ROWS ONLY
+""");
+ }
+
+ public override async Task Where_subquery_with_ElementAtOrDefault_equality_to_null_with_composite_key(bool async)
+ {
+ await base.Where_subquery_with_ElementAtOrDefault_equality_to_null_with_composite_key(async);
+
+ AssertSql(
+"""
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE NOT (EXISTS (
+ SELECT 1
+ FROM (
+ SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], N'Gear' AS [Discriminator]
+ FROM [Gears] AS [g]
+ UNION ALL
+ SELECT [o].[Nickname], [o].[SquadId], [o].[AssignedCityName], [o].[CityOfBirthName], [o].[FullName], [o].[HasSoulPatch], [o].[LeaderNickname], [o].[LeaderSquadId], [o].[Rank], N'Officer' AS [Discriminator]
+ FROM [Officers] AS [o]
+ ) AS [t]
+ WHERE [s].[Id] = [t].[SquadId]
+ ORDER BY [t].[Nickname]
+ OFFSET 2 ROWS))
+""");
+ }
+
+ public override async Task Where_subquery_with_ElementAt_using_column_as_index(bool async)
+ {
+ await base.Where_subquery_with_ElementAt_using_column_as_index(async);
+
+ AssertSql(
+"""
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE (
+ SELECT [t].[Nickname]
+ FROM (
+ SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], N'Gear' AS [Discriminator]
+ FROM [Gears] AS [g]
+ UNION ALL
+ SELECT [o].[Nickname], [o].[SquadId], [o].[AssignedCityName], [o].[CityOfBirthName], [o].[FullName], [o].[HasSoulPatch], [o].[LeaderNickname], [o].[LeaderSquadId], [o].[Rank], N'Officer' AS [Discriminator]
+ FROM [Officers] AS [o]
+ ) AS [t]
+ WHERE [s].[Id] = [t].[SquadId]
+ ORDER BY [t].[Nickname]
+ OFFSET [s].[Id] ROWS FETCH NEXT 1 ROWS ONLY) = N'Cole Train'
+""");
+ }
+
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs
index 4322839499a..0e755c2a821 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs
@@ -11195,6 +11195,96 @@ WHERE [o].[Nickname] IS NOT NULL
""");
}
+ public override async Task ElementAt_basic_with_OrderBy(bool async)
+ {
+ await base.ElementAt_basic_with_OrderBy(async);
+
+ AssertSql(
+"""
+@__p_0='0'
+
+SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE
+ WHEN [o].[Nickname] IS NOT NULL THEN N'Officer'
+END AS [Discriminator]
+FROM [Gears] AS [g]
+LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId]
+ORDER BY [g].[FullName]
+OFFSET @__p_0 ROWS FETCH NEXT 1 ROWS ONLY
+""");
+ }
+
+ public override async Task ElementAtOrDefault_basic_with_OrderBy(bool async)
+ {
+ await base.ElementAtOrDefault_basic_with_OrderBy(async);
+
+ AssertSql(
+"""
+@__p_0='1'
+
+SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE
+ WHEN [o].[Nickname] IS NOT NULL THEN N'Officer'
+END AS [Discriminator]
+FROM [Gears] AS [g]
+LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId]
+ORDER BY [g].[FullName]
+OFFSET @__p_0 ROWS FETCH NEXT 1 ROWS ONLY
+""");
+ }
+
+ public override async Task ElementAtOrDefault_basic_with_OrderBy_parameter(bool async)
+ {
+ await base.ElementAtOrDefault_basic_with_OrderBy_parameter(async);
+
+ AssertSql(
+"""
+@__p_0='2'
+
+SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE
+ WHEN [o].[Nickname] IS NOT NULL THEN N'Officer'
+END AS [Discriminator]
+FROM [Gears] AS [g]
+LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId]
+ORDER BY [g].[FullName]
+OFFSET @__p_0 ROWS FETCH NEXT 1 ROWS ONLY
+""");
+ }
+
+ public override async Task Where_subquery_with_ElementAtOrDefault_equality_to_null_with_composite_key(bool async)
+ {
+ await base.Where_subquery_with_ElementAtOrDefault_equality_to_null_with_composite_key(async);
+
+ AssertSql(
+"""
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE NOT (EXISTS (
+ SELECT 1
+ FROM [Gears] AS [g]
+ LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId]
+ WHERE [s].[Id] = [g].[SquadId]
+ ORDER BY [g].[Nickname]
+ OFFSET 2 ROWS))
+""");
+ }
+
+ public override async Task Where_subquery_with_ElementAt_using_column_as_index(bool async)
+ {
+ await base.Where_subquery_with_ElementAt_using_column_as_index(async);
+
+ AssertSql(
+"""
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name]
+FROM [Squads] AS [s]
+WHERE (
+ SELECT [g].[Nickname]
+ FROM [Gears] AS [g]
+ LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId]
+ WHERE [s].[Id] = [g].[SquadId]
+ ORDER BY [g].[Nickname]
+ OFFSET [s].[Id] ROWS FETCH NEXT 1 ROWS ONLY) = N'Cole Train'
+""");
+ }
+
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsQuerySqlServerTest.cs
index 23a82d8e294..0850234d7f5 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsQuerySqlServerTest.cs
@@ -20,6 +20,8 @@ public TemporalComplexNavigationsCollectionsQuerySqlServerTest(
protected override Expression RewriteServerQueryExpression(Expression serverQueryExpression)
{
+ serverQueryExpression = base.RewriteServerQueryExpression(serverQueryExpression);
+
var temporalEntityTypes = new List
{
typeof(Level1),
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs
index 530015ae98b..e1a93618956 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs
@@ -20,6 +20,8 @@ public TemporalComplexNavigationsCollectionsSharedTypeQuerySqlServerTest(
protected override Expression RewriteServerQueryExpression(Expression serverQueryExpression)
{
+ serverQueryExpression = base.RewriteServerQueryExpression(serverQueryExpression);
+
var temporalEntityTypes = new List
{
typeof(Level1),
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalFiltersInheritanceQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalFiltersInheritanceQuerySqlServerTest.cs
index e99be2c055e..3c922201d74 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalFiltersInheritanceQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalFiltersInheritanceQuerySqlServerTest.cs
@@ -20,6 +20,8 @@ public TemporalFiltersInheritanceQuerySqlServerTest(
protected override Expression RewriteServerQueryExpression(Expression serverQueryExpression)
{
+ serverQueryExpression = base.RewriteServerQueryExpression(serverQueryExpression);
+
var temporalEntityTypes = new List
{
typeof(Animal),
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs
index f6eb7d67917..b2b1344664b 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs
@@ -21,6 +21,8 @@ public TemporalGearsOfWarQuerySqlServerTest(TemporalGearsOfWarQuerySqlServerFixt
protected override Expression RewriteServerQueryExpression(Expression serverQueryExpression)
{
+ serverQueryExpression = base.RewriteServerQueryExpression(serverQueryExpression);
+
var temporalEntityTypes = new List
{
typeof(City),
@@ -9789,6 +9791,74 @@ public override async Task EF_Property_based_Include_navigation_on_derived_type(
""");
}
+ // Sequence contains no elements due to temporal filter
+ public override Task ElementAt_basic_with_OrderBy(bool async)
+ => Task.CompletedTask;
+
+ public override async Task ElementAtOrDefault_basic_with_OrderBy(bool async)
+ {
+ await base.ElementAtOrDefault_basic_with_OrderBy(async);
+
+ AssertSql(
+"""
+@__p_0='1'
+
+SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank]
+FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g]
+ORDER BY [g].[FullName]
+OFFSET @__p_0 ROWS FETCH NEXT 1 ROWS ONLY
+""");
+ }
+
+ public override async Task ElementAtOrDefault_basic_with_OrderBy_parameter(bool async)
+ {
+ await base.ElementAtOrDefault_basic_with_OrderBy_parameter(async);
+
+ AssertSql(
+"""
+@__p_0='2'
+
+SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank]
+FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g]
+ORDER BY [g].[FullName]
+OFFSET @__p_0 ROWS FETCH NEXT 1 ROWS ONLY
+""");
+ }
+
+ public override async Task Where_subquery_with_ElementAtOrDefault_equality_to_null_with_composite_key(bool async)
+ {
+ await base.Where_subquery_with_ElementAtOrDefault_equality_to_null_with_composite_key(async);
+
+ AssertSql(
+"""
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name], [s].[PeriodEnd], [s].[PeriodStart]
+FROM [Squads] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [s]
+WHERE NOT (EXISTS (
+ SELECT 1
+ FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g]
+ WHERE [s].[Id] = [g].[SquadId]
+ ORDER BY [g].[Nickname]
+ OFFSET 2 ROWS))
+""");
+ }
+
+ public override async Task Where_subquery_with_ElementAt_using_column_as_index(bool async)
+ {
+ await base.Where_subquery_with_ElementAt_using_column_as_index(async);
+
+ AssertSql(
+"""
+SELECT [s].[Id], [s].[Banner], [s].[Banner5], [s].[InternalNumber], [s].[Name], [s].[PeriodEnd], [s].[PeriodStart]
+FROM [Squads] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [s]
+WHERE (
+ SELECT [g].[Nickname]
+ FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g]
+ WHERE [s].[Id] = [g].[SquadId]
+ ORDER BY [g].[Nickname]
+ OFFSET [s].[Id] ROWS FETCH NEXT 1 ROWS ONLY) = N'Cole Train'
+""");
+ }
+
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalManyToManyQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalManyToManyQuerySqlServerTest.cs
index 929c30c7513..4a6acd18c37 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalManyToManyQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalManyToManyQuerySqlServerTest.cs
@@ -19,6 +19,8 @@ public TemporalManyToManyQuerySqlServerTest(TemporalManyToManyQuerySqlServerFixt
protected override Expression RewriteServerQueryExpression(Expression serverQueryExpression)
{
+ serverQueryExpression = base.RewriteServerQueryExpression(serverQueryExpression);
+
var temporalEntityTypes = new List
{
typeof(EntityOne),
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalOwnedQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalOwnedQuerySqlServerTest.cs
index 9b322cc1b7d..2b5b3ffb323 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalOwnedQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalOwnedQuerySqlServerTest.cs
@@ -18,6 +18,8 @@ protected override bool CanExecuteQueryString
protected override Expression RewriteServerQueryExpression(Expression serverQueryExpression)
{
+ serverQueryExpression = base.RewriteServerQueryExpression(serverQueryExpression);
+
var temporalEntityTypes = new List
{
typeof(OwnedPerson),
diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs
index 6f3832eb387..7ca361c924a 100644
--- a/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs
@@ -1,6 +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.Data.Sqlite;
using Microsoft.EntityFrameworkCore.Sqlite.Internal;
using Microsoft.EntityFrameworkCore.TestModels.GearsOfWarModel;
@@ -9347,6 +9348,88 @@ public override async Task EF_Property_based_Include_navigation_on_derived_type(
""");
}
+ public override async Task ElementAt_basic_with_OrderBy(bool async)
+ {
+ await base.ElementAt_basic_with_OrderBy(async);
+
+ AssertSql(
+"""
+@__p_0='0'
+
+SELECT "g"."Nickname", "g"."SquadId", "g"."AssignedCityName", "g"."CityOfBirthName", "g"."Discriminator", "g"."FullName", "g"."HasSoulPatch", "g"."LeaderNickname", "g"."LeaderSquadId", "g"."Rank"
+FROM "Gears" AS "g"
+ORDER BY "g"."FullName"
+LIMIT 1 OFFSET @__p_0
+""");
+ }
+
+ public override async Task ElementAtOrDefault_basic_with_OrderBy(bool async)
+ {
+ await base.ElementAtOrDefault_basic_with_OrderBy(async);
+
+ AssertSql(
+"""
+@__p_0='1'
+
+SELECT "g"."Nickname", "g"."SquadId", "g"."AssignedCityName", "g"."CityOfBirthName", "g"."Discriminator", "g"."FullName", "g"."HasSoulPatch", "g"."LeaderNickname", "g"."LeaderSquadId", "g"."Rank"
+FROM "Gears" AS "g"
+ORDER BY "g"."FullName"
+LIMIT 1 OFFSET @__p_0
+""");
+ }
+
+ public override async Task ElementAtOrDefault_basic_with_OrderBy_parameter(bool async)
+ {
+ await base.ElementAtOrDefault_basic_with_OrderBy_parameter(async);
+
+ AssertSql(
+"""
+@__p_0='2'
+
+SELECT "g"."Nickname", "g"."SquadId", "g"."AssignedCityName", "g"."CityOfBirthName", "g"."Discriminator", "g"."FullName", "g"."HasSoulPatch", "g"."LeaderNickname", "g"."LeaderSquadId", "g"."Rank"
+FROM "Gears" AS "g"
+ORDER BY "g"."FullName"
+LIMIT 1 OFFSET @__p_0
+""");
+ }
+
+ public override async Task Where_subquery_with_ElementAtOrDefault_equality_to_null_with_composite_key(bool async)
+ {
+ await base.Where_subquery_with_ElementAtOrDefault_equality_to_null_with_composite_key(async);
+
+ AssertSql(
+"""
+SELECT "s"."Id", "s"."Banner", "s"."Banner5", "s"."InternalNumber", "s"."Name"
+FROM "Squads" AS "s"
+WHERE NOT (EXISTS (
+ SELECT 1
+ FROM "Gears" AS "g"
+ WHERE "s"."Id" = "g"."SquadId"
+ ORDER BY "g"."Nickname"
+ LIMIT -1 OFFSET 2))
+""");
+ }
+
+ public override async Task Where_subquery_with_ElementAt_using_column_as_index(bool async)
+ {
+ var message = (await Assert.ThrowsAsync(
+ () => base.Where_subquery_with_ElementAt_using_column_as_index(async))).Message;
+
+ Assert.Equal("SQLite Error 1: 'no such column: s.Id'.", message);
+
+ AssertSql(
+"""
+SELECT "s"."Id", "s"."Banner", "s"."Banner5", "s"."InternalNumber", "s"."Name"
+FROM "Squads" AS "s"
+WHERE (
+ SELECT "g"."Nickname"
+ FROM "Gears" AS "g"
+ WHERE "s"."Id" = "g"."SquadId"
+ ORDER BY "g"."Nickname"
+ LIMIT 1 OFFSET "s"."Id") = 'Cole Train'
+""");
+ }
+
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}
diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/TPCGearsOfWarQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/TPCGearsOfWarQuerySqliteTest.cs
index 2e12f285fa9..af9fc4e0122 100644
--- a/test/EFCore.Sqlite.FunctionalTests/Query/TPCGearsOfWarQuerySqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/Query/TPCGearsOfWarQuerySqliteTest.cs
@@ -1,6 +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.Data.Sqlite;
using Microsoft.EntityFrameworkCore.Sqlite.Internal;
namespace Microsoft.EntityFrameworkCore.Query;
@@ -394,6 +395,32 @@ public override Task Where_TimeOnly_subtract_TimeOnly(bool async)
// TimeSpan. Issue #18844.
=> AssertTranslationFailed(() => base.Where_TimeOnly_subtract_TimeOnly(async));
+ public override async Task Where_subquery_with_ElementAt_using_column_as_index(bool async)
+ {
+ var message = (await Assert.ThrowsAsync(
+ () => base.Where_subquery_with_ElementAt_using_column_as_index(async))).Message;
+
+ Assert.Equal("SQLite Error 1: 'no such column: s.Id'.", message);
+
+ AssertSql(
+"""
+SELECT "s"."Id", "s"."Banner", "s"."Banner5", "s"."InternalNumber", "s"."Name"
+FROM "Squads" AS "s"
+WHERE (
+ SELECT "t"."Nickname"
+ FROM (
+ SELECT "g"."Nickname", "g"."SquadId", "g"."AssignedCityName", "g"."CityOfBirthName", "g"."FullName", "g"."HasSoulPatch", "g"."LeaderNickname", "g"."LeaderSquadId", "g"."Rank", 'Gear' AS "Discriminator"
+ FROM "Gears" AS "g"
+ UNION ALL
+ SELECT "o"."Nickname", "o"."SquadId", "o"."AssignedCityName", "o"."CityOfBirthName", "o"."FullName", "o"."HasSoulPatch", "o"."LeaderNickname", "o"."LeaderSquadId", "o"."Rank", 'Officer' AS "Discriminator"
+ FROM "Officers" AS "o"
+ ) AS "t"
+ WHERE "s"."Id" = "t"."SquadId"
+ ORDER BY "t"."Nickname"
+ LIMIT 1 OFFSET "s"."Id") = 'Cole Train'
+""");
+ }
+
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}
diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/TPTGearsOfWarQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/TPTGearsOfWarQuerySqliteTest.cs
index f18b7a6c8bb..4727d9b01ec 100644
--- a/test/EFCore.Sqlite.FunctionalTests/Query/TPTGearsOfWarQuerySqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/Query/TPTGearsOfWarQuerySqliteTest.cs
@@ -1,6 +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.Data.Sqlite;
using Microsoft.EntityFrameworkCore.Sqlite.Internal;
namespace Microsoft.EntityFrameworkCore.Query;
@@ -394,6 +395,27 @@ public override Task Where_TimeOnly_subtract_TimeOnly(bool async)
// TimeSpan. Issue #18844.
=> AssertTranslationFailed(() => base.Where_TimeOnly_subtract_TimeOnly(async));
+ public override async Task Where_subquery_with_ElementAt_using_column_as_index(bool async)
+ {
+ var message = (await Assert.ThrowsAsync(
+ () => base.Where_subquery_with_ElementAt_using_column_as_index(async))).Message;
+
+ Assert.Equal("SQLite Error 1: 'no such column: s.Id'.", message);
+
+ AssertSql(
+"""
+SELECT "s"."Id", "s"."Banner", "s"."Banner5", "s"."InternalNumber", "s"."Name"
+FROM "Squads" AS "s"
+WHERE (
+ SELECT "g"."Nickname"
+ FROM "Gears" AS "g"
+ LEFT JOIN "Officers" AS "o" ON "g"."Nickname" = "o"."Nickname" AND "g"."SquadId" = "o"."SquadId"
+ WHERE "s"."Id" = "g"."SquadId"
+ ORDER BY "g"."Nickname"
+ LIMIT 1 OFFSET "s"."Id") = 'Cole Train'
+""");
+ }
+
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}