Skip to content

Add support for OData group by queries #2322

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/NHibernate.Test/Async/Linq/ByMethod/GroupByTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,48 @@ public async Task GroupByOrderByKeySelectToClassAsync()
.ToListAsync());
}

[Test]
public async Task SelectArrayIndexBeforeGroupByAsync()
{
var result = db.Orders
.SelectMany(o => o.OrderLines.Select(c => c.Id).DefaultIfEmpty().Select(c => new object[] {c, o}))
.GroupBy(g => g[0], g => (Order) g[1])
.Select(g => new[] {g.Key, g.Count(), g.Max(x => x.OrderDate)});

Assert.True(await (result.AnyAsync()));
}

[Test]
public async Task SelectMemberInitBeforeGroupByAsync()
{
var result = await (db.Orders
.Select(o => new OrderGroup {OrderId = o.OrderId, OrderDate = o.OrderDate})
.GroupBy(o => o.OrderId)
.Select(g => new OrderGroup {OrderId = g.Key, OrderDate = g.Max(o => o.OrderDate)})
.ToListAsync());

Assert.True(result.Any());
}

[Test]
public async Task SelectNewBeforeGroupByAsync()
{
var result = await (db.Orders
.Select(o => new {o.OrderId, o.OrderDate})
.GroupBy(o => o.OrderId)
.Select(g => new {OrderId = g.Key, OrderDate = g.Max(o => o.OrderDate)})
.ToListAsync());

Assert.True(result.Any());
}

private class OrderGroup
{
public int OrderId { get; set; }

public DateTime? OrderDate { get; set; }
}

private class GroupInfo
{
public object Key { get; set; }
Expand Down
164 changes: 164 additions & 0 deletions src/NHibernate.Test/Async/Linq/ODataTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by AsyncGenerator.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------


using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.OData;
using Microsoft.AspNet.OData.Builder;
using Microsoft.AspNet.OData.Extensions;
using Microsoft.AspNet.OData.Query;
using Microsoft.AspNet.OData.Query.Expressions;
using Microsoft.AspNetCore.Http;
using Microsoft.OData.Edm;
using NHibernate.DomainModel.Northwind.Entities;
using NUnit.Framework;
using NHibernate.Linq;

namespace NHibernate.Test.Linq
{
using System.Threading.Tasks;
[TestFixture]
public class ODataTestsAsync : LinqTestCase
{
private IEdmModel _edmModel;

protected override void OnSetUp()
{
base.OnSetUp();

_edmModel = CreatEdmModel();
}

[TestCase("$apply=groupby((Customer/CustomerId))", 89)]
[TestCase("$apply=groupby((Customer/CustomerId))&$orderby=Customer/CustomerId", 89)]
[TestCase("$apply=groupby((Customer/CustomerId, ShippingAddress/PostalCode), aggregate(OrderId with average as Average, Employee/EmployeeId with max as Max))", 89)]
[TestCase("$apply=groupby((Customer/CustomerId), aggregate(OrderId with sum as Total))&$skip=2", 87)]
public async Task OrderGroupByAsync(string queryString, int expectedRows)
{
var query = ApplyFilter(session.Query<Order>(), queryString);
Assert.That(query, Is.AssignableTo<IQueryable<DynamicTypeWrapper>>());

var results = await (((IQueryable<DynamicTypeWrapper>) query).ToListAsync());
Assert.That(results, Has.Count.EqualTo(expectedRows));
}

private IQueryable ApplyFilter<T>(IQueryable<T> query, string queryString)
{
var context = new ODataQueryContext(CreatEdmModel(), typeof(T), null) { };
var dataQuerySettings = new ODataQuerySettings {HandleNullPropagation = HandleNullPropagationOption.False};
var serviceProvider = new ODataServiceProvider(
new Dictionary<System.Type, object>()
{
{typeof(DefaultQuerySettings), new DefaultQuerySettings()},
{typeof(ODataOptions), new ODataOptions()},
{typeof(IEdmModel), _edmModel},
{typeof(ODataQuerySettings), dataQuerySettings},
});

HttpContext httpContext = new DefaultHttpContext();
httpContext.ODataFeature().RequestContainer = serviceProvider;
httpContext.RequestServices = serviceProvider;
var request = httpContext.Request;
Uri requestUri = new Uri($"http://localhost/?{queryString}");
request.Method = HttpMethods.Get;
request.Scheme = requestUri.Scheme;
request.Host = new HostString(requestUri.Host);
request.QueryString = new QueryString(requestUri.Query);
request.Path = new PathString(requestUri.AbsolutePath);
var options = new ODataQueryOptions(context, request);

return options.ApplyTo(query, dataQuerySettings);
}

private static IEdmModel CreatEdmModel()
{
var builder = new ODataConventionModelBuilder();

var adressModel = builder.ComplexType<Address>();
adressModel.Property(o => o.City);
adressModel.Property(o => o.Country);
adressModel.Property(o => o.Fax);
adressModel.Property(o => o.PhoneNumber);
adressModel.Property(o => o.PostalCode);
adressModel.Property(o => o.Region);
adressModel.Property(o => o.Street);

var customerModel = builder.EntitySet<Customer>(nameof(Customer));
customerModel.EntityType.HasKey(o => o.CustomerId);
customerModel.EntityType.Property(o => o.CompanyName);
customerModel.EntityType.Property(o => o.ContactTitle);
customerModel.EntityType.ComplexProperty(o => o.Address);
customerModel.EntityType.HasMany(o => o.Orders);

var orderModel = builder.EntitySet<Order>(nameof(Order));
orderModel.EntityType.HasKey(o => o.OrderId);
orderModel.EntityType.Property(o => o.Freight);
orderModel.EntityType.Property(o => o.OrderDate);
orderModel.EntityType.Property(o => o.RequiredDate);
orderModel.EntityType.Property(o => o.ShippedTo);
orderModel.EntityType.Property(o => o.ShippingDate);
orderModel.EntityType.ComplexProperty(o => o.ShippingAddress);
orderModel.EntityType.HasRequired(o => o.Customer);
orderModel.EntityType.HasOptional(o => o.Employee);

var employeeModel = builder.EntitySet<Employee>(nameof(Employee));
employeeModel.EntityType.HasKey(o => o.EmployeeId);
employeeModel.EntityType.Property(o => o.BirthDate);
employeeModel.EntityType.Property(o => o.Extension);
employeeModel.EntityType.Property(o => o.FirstName);
employeeModel.EntityType.Property(o => o.HireDate);
employeeModel.EntityType.Property(o => o.LastName);
employeeModel.EntityType.Property(o => o.Notes);
employeeModel.EntityType.Property(o => o.Title);
employeeModel.EntityType.HasMany(o => o.Orders);

return builder.GetEdmModel();
}

private class ODataServiceProvider : IServiceProvider
{
private readonly Dictionary<System.Type, object> _singletonObjects = new Dictionary<System.Type, object>();

public ODataServiceProvider(Dictionary<System.Type, object> singletonObjects)
{
_singletonObjects = singletonObjects;
}

public object GetService(System.Type serviceType)
{
if (_singletonObjects.TryGetValue(serviceType, out var service))
{
return service;
}

var ctor = serviceType.GetConstructor(new System.Type[0]);
if (ctor != null)
{
return ctor.Invoke(new object[0]);
}

ctor = serviceType.GetConstructor(new[] { typeof(DefaultQuerySettings) });
if (ctor != null)
{
return ctor.Invoke(new object[] { GetService(typeof(DefaultQuerySettings)) });
}

ctor = serviceType.GetConstructor(new[] { typeof(IServiceProvider) });
if (ctor != null)
{
return ctor.Invoke(new object[] { this });
}

return null;
}
}
}
}
42 changes: 42 additions & 0 deletions src/NHibernate.Test/Linq/ByMethod/GroupByTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,48 @@ public void FetchBeforeGroupBy()
Assert.True(result.Any());
}

[Test]
public void SelectArrayIndexBeforeGroupBy()
{
var result = db.Orders
.SelectMany(o => o.OrderLines.Select(c => c.Id).DefaultIfEmpty().Select(c => new object[] {c, o}))
.GroupBy(g => g[0], g => (Order) g[1])
.Select(g => new[] {g.Key, g.Count(), g.Max(x => x.OrderDate)});

Assert.True(result.Any());
}

[Test]
public void SelectMemberInitBeforeGroupBy()
{
var result = db.Orders
.Select(o => new OrderGroup {OrderId = o.OrderId, OrderDate = o.OrderDate})
.GroupBy(o => o.OrderId)
.Select(g => new OrderGroup {OrderId = g.Key, OrderDate = g.Max(o => o.OrderDate)})
.ToList();

Assert.True(result.Any());
}

[Test]
public void SelectNewBeforeGroupBy()
{
var result = db.Orders
.Select(o => new {o.OrderId, o.OrderDate})
.GroupBy(o => o.OrderId)
.Select(g => new {OrderId = g.Key, OrderDate = g.Max(o => o.OrderDate)})
.ToList();

Assert.True(result.Any());
}

private class OrderGroup
{
public int OrderId { get; set; }

public DateTime? OrderDate { get; set; }
}

private class GroupInfo
{
public object Key { get; set; }
Expand Down
Loading