Skip to content

Commit 848b915

Browse files
authored
Modification to allow Linq subqueries to have paging (OrderBy/Take) (#2480)
Fixes #2479
1 parent 9fc784e commit 848b915

File tree

3 files changed

+96
-3
lines changed

3 files changed

+96
-3
lines changed

src/NHibernate.Test/Async/Linq/WhereSubqueryTests.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@
1111
using System;
1212
using System.Linq;
1313
using System.Linq.Expressions;
14+
using NHibernate.Dialect;
1415
using NHibernate.DomainModel.Northwind.Entities;
15-
using NUnit.Framework;
1616
using NHibernate.Linq;
17+
using NUnit.Framework;
1718

1819
namespace NHibernate.Test.Linq
1920
{
@@ -465,6 +466,27 @@ where subquery.Any(x => x.OrderId == order.OrderId)
465466
Assert.That(query.Count, Is.EqualTo(61));
466467
}
467468

469+
[Test(Description = "GH2479")]
470+
public async Task OrdersWithSubquery11Async()
471+
{
472+
if (Dialect is MySQLDialect)
473+
Assert.Ignore("MySQL does not support LIMIT in subqueries.");
474+
if (Dialect is MsSqlCeDialect)
475+
Assert.Ignore("MS SQL CE does not support sorting on a subquery.");
476+
477+
var ordersQuery = db.Orders
478+
.OrderByDescending(x => x.OrderLines.Count).ThenBy(x => x.OrderId)
479+
.Take(2);
480+
481+
var orderLineQuery = ordersQuery.SelectMany(x => x.OrderLines);
482+
var productsNotInLargestOrders = await (db.Products
483+
.Where(x => orderLineQuery.All(p => p.Product != x))
484+
.OrderBy(x => x.ProductId)
485+
.ToListAsync());
486+
487+
Assert.That(productsNotInLargestOrders.Count, Is.EqualTo(49), nameof(productsNotInLargestOrders));
488+
}
489+
468490
[Test(Description = "NH-2654")]
469491
public async Task CategoriesWithDiscountedProductsAsync()
470492
{

src/NHibernate.Test/Linq/WhereSubqueryTests.cs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
using System;
22
using System.Linq;
33
using System.Linq.Expressions;
4+
using NHibernate.Dialect;
45
using NHibernate.DomainModel.Northwind.Entities;
6+
using NHibernate.Linq;
57
using NUnit.Framework;
68

79
namespace NHibernate.Test.Linq
@@ -509,6 +511,74 @@ where subquery.Any(x => x.OrderId == order.OrderId)
509511
Assert.That(query.Count, Is.EqualTo(61));
510512
}
511513

514+
[Test(Description = "GH2479")]
515+
public void OrdersWithSubquery9()
516+
{
517+
if (Dialect is MySQLDialect)
518+
Assert.Ignore("MySQL does not support LIMIT in subqueries.");
519+
520+
var ordersQuery = db.Orders
521+
.Where(x => x.Employee.EmployeeId > 5)
522+
.OrderBy(x => x.OrderId)
523+
.Take(2);
524+
525+
var orderLinesFuture = db.OrderLines
526+
.Where(x => ordersQuery.Any(o => o == x.Order))
527+
.OrderBy(x => x.Id)
528+
.ToFuture();
529+
530+
var orders = ordersQuery.ToFuture().ToList();
531+
var orderLines = orderLinesFuture.ToList();
532+
533+
Assert.That(orders.Count, Is.EqualTo(2), nameof(orders));
534+
Assert.That(orderLines.Count, Is.EqualTo(6), nameof(orderLines));
535+
}
536+
537+
[Test(Description = "GH2479")]
538+
public void OrdersWithSubquery10()
539+
{
540+
if (Dialect is MySQLDialect)
541+
Assert.Ignore("MySQL does not support LIMIT in subqueries.");
542+
543+
var ordersQuery = db.Orders
544+
.Where(x => x.Employee.EmployeeId > 5)
545+
.OrderBy(x => x.OrderId)
546+
.Take(2);
547+
548+
var productsQuery = ordersQuery.SelectMany(x => x.OrderLines).Select(x => x.Product);
549+
var productsFuture = db.Products
550+
.Where(x => productsQuery.Contains(x))
551+
.OrderBy(x => x.ProductId)
552+
.ToFuture();
553+
554+
var orders = ordersQuery.ToFuture().ToList();
555+
var products = productsFuture.ToList();
556+
557+
Assert.That(orders.Count, Is.EqualTo(2), nameof(orders));
558+
Assert.That(products.Count, Is.EqualTo(6), nameof(products));
559+
}
560+
561+
[Test(Description = "GH2479")]
562+
public void OrdersWithSubquery11()
563+
{
564+
if (Dialect is MySQLDialect)
565+
Assert.Ignore("MySQL does not support LIMIT in subqueries.");
566+
if (Dialect is MsSqlCeDialect)
567+
Assert.Ignore("MS SQL CE does not support sorting on a subquery.");
568+
569+
var ordersQuery = db.Orders
570+
.OrderByDescending(x => x.OrderLines.Count).ThenBy(x => x.OrderId)
571+
.Take(2);
572+
573+
var orderLineQuery = ordersQuery.SelectMany(x => x.OrderLines);
574+
var productsNotInLargestOrders = db.Products
575+
.Where(x => orderLineQuery.All(p => p.Product != x))
576+
.OrderBy(x => x.ProductId)
577+
.ToList();
578+
579+
Assert.That(productsNotInLargestOrders.Count, Is.EqualTo(49), nameof(productsNotInLargestOrders));
580+
}
581+
512582
[Test(Description = "NH-2654")]
513583
public void CategoriesWithDiscountedProducts()
514584
{

src/NHibernate/Linq/GroupBy/PagingRewriter.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public static void ReWrite(QueryModel queryModel)
2828

2929
private static void FlattenSubQuery(SubQueryExpression subQueryExpression, QueryModel queryModel)
3030
{
31-
// we can not flattern subquery if outer query has body clauses.
31+
// we can not flatten subquery if outer query has body clauses.
3232
var subQueryModel = subQueryExpression.QueryModel;
3333
var subQueryMainFromClause = subQueryModel.MainFromClause;
3434
if (queryModel.BodyClauses.Count == 0)
@@ -53,7 +53,8 @@ private static void FlattenSubQuery(SubQueryExpression subQueryExpression, Query
5353
var where = new WhereClause(new SubQueryExpression(newSubQueryModel));
5454
queryModel.BodyClauses.Add(where);
5555

56-
if (!queryModel.BodyClauses.OfType<OrderByClause>().Any())
56+
if (!queryModel.BodyClauses.OfType<OrderByClause>().Any() &&
57+
!(queryModel.ResultOperators.Count == 1 && queryModel.ResultOperators.All(x => x is AnyResultOperator || x is ContainsResultOperator || x is AllResultOperator)))
5758
{
5859
var orderByClauses = subQueryModel.BodyClauses.OfType<OrderByClause>();
5960
foreach (var orderByClause in orderByClauses)

0 commit comments

Comments
 (0)