Skip to content
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

support IAsyncenumerable #988

Merged
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
3 changes: 2 additions & 1 deletion src/Microsoft.AspNetCore.OData/Common/TypeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,8 @@ Type collectionInterface
.Union(new[] { clrType })
.FirstOrDefault(
t => t.IsGenericType
&& t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
&& (t.GetGenericTypeDefinition() == typeof(IEnumerable<>)
|| t.GetGenericTypeDefinition() == typeof(IAsyncEnumerable<>)));

if (collectionInterface != null)
{
Expand Down
15 changes: 13 additions & 2 deletions src/Microsoft.AspNetCore.OData/Edm/DefaultODataTypeMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,20 @@ private IEdmType GetEdmType(IEdmModel edmModel, Type clrType, bool testCollectio
}

Type enumerableOfT = ExtractGenericInterface(clrType, typeof(IEnumerable<>));
if (enumerableOfT != null)
Type asyncEnumerableOfT = ExtractGenericInterface(clrType, typeof(IAsyncEnumerable<>));

if (enumerableOfT != null || asyncEnumerableOfT != null)
{
Type elementClrType = enumerableOfT.GetGenericArguments()[0];
Type elementClrType = null;

if (enumerableOfT != null)
ElizabethOkerio marked this conversation as resolved.
Show resolved Hide resolved
{
elementClrType = enumerableOfT.GetGenericArguments()[0];
}
else
{
elementClrType = asyncEnumerableOfT.GetGenericArguments()[0];
}

// IEnumerable<SelectExpandWrapper<T>> is a collection of T.
if (elementClrType.IsSelectExpandWrapper(out entityType))
Expand Down
19 changes: 18 additions & 1 deletion src/Microsoft.AspNetCore.OData/Formatter/ResourceSetContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//------------------------------------------------------------------------------

using System.Collections;
using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.OData.Extensions;
using Microsoft.AspNetCore.OData.Formatter.Serialization;
Expand Down Expand Up @@ -55,11 +56,27 @@ internal static ResourceSetContext Create(ODataSerializerContext writeContext, I
{
Request = writeContext.Request,
EntitySetBase = writeContext.NavigationSource as IEdmEntitySetBase,
// Url = writeContext.Url,
ResourceSetInstance = resourceSetInstance
};

return resourceSetContext;
}

/// <summary>
/// Create a <see cref="ResourceSetContext"/> from an <see cref="ODataSerializerContext"/> and <see cref="IAsyncEnumerable{T}"/>.
/// </summary>
/// <param name="writeContext">The serializer context.</param>
ElizabethOkerio marked this conversation as resolved.
Show resolved Hide resolved
/// <param name="resourceSetInstance">The instance representing the resourceSet being written.</param>
/// <returns>A new <see cref="ResourceSetContext"/>.</returns>
/// <remarks>This signature uses types that are AspNetCore-specific.</remarks>
internal static ResourceSetContext Create(ODataSerializerContext writeContext, IAsyncEnumerable<object> resourceSetInstance)
{
return new ResourceSetContext
{
Request = writeContext.Request,
EntitySetBase = writeContext.NavigationSource as IEdmEntitySetBase,
ResourceSetInstance = resourceSetInstance
};
}
}
}

Large diffs are not rendered by default.

27 changes: 27 additions & 0 deletions src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4309,6 +4309,15 @@
<returns>A new <see cref="T:Microsoft.AspNetCore.OData.Formatter.ResourceSetContext"/>.</returns>
<remarks>This signature uses types that are AspNetCore-specific.</remarks>
</member>
<member name="M:Microsoft.AspNetCore.OData.Formatter.ResourceSetContext.Create(Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext,System.Collections.Generic.IAsyncEnumerable{System.Object})">
<summary>
Create a <see cref="T:Microsoft.AspNetCore.OData.Formatter.ResourceSetContext"/> from an <see cref="T:Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext"/> and <see cref="T:System.Collections.Generic.IAsyncEnumerable`1"/>.
</summary>
<param name="writeContext">The serializer context.</param>
<param name="resourceSetInstance">The instance representing the resourceSet being written.</param>
<returns>A new <see cref="T:Microsoft.AspNetCore.OData.Formatter.ResourceSetContext"/>.</returns>
<remarks>This signature uses types that are AspNetCore-specific.</remarks>
</member>
<member name="T:Microsoft.AspNetCore.OData.Formatter.Serialization.IODataEdmTypeSerializer">
<summary>
Represents an <see cref="T:Microsoft.AspNetCore.OData.Formatter.Serialization.IODataSerializer"/> that serializes instances of objects backed by an <see cref="T:Microsoft.OData.Edm.IEdmType"/>.
Expand Down Expand Up @@ -4914,6 +4923,15 @@
<param name="writeContext">The serializer context.</param>
<returns>The created <see cref="T:Microsoft.OData.ODataResourceSet"/> object.</returns>
</member>
<member name="M:Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSetSerializer.CreateResourceSet(System.Collections.Generic.IAsyncEnumerable{System.Object},Microsoft.OData.Edm.IEdmCollectionTypeReference,Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext)">
<summary>
Create the <see cref="T:Microsoft.OData.ODataResourceSet"/> to be written for the given resourceSet instance.
</summary>
<param name="resourceSetInstance">The instance representing the resourceSet being written.</param>
<param name="resourceSetType">The EDM type of the resourceSet being written.</param>
<param name="writeContext">The serializer context.</param>
<returns>The created <see cref="T:Microsoft.OData.ODataResourceSet"/> object.</returns>
</member>
<member name="M:Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSetSerializer.GetNextLinkGenerator(Microsoft.OData.ODataResourceSetBase,System.Collections.IEnumerable,Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext)">
<summary>
Creates a function that takes in an object and generates nextlink uri.
Expand All @@ -4923,6 +4941,15 @@
<param name="writeContext">The serializer context.</param>
<returns>The function that generates the NextLink from an object.</returns>
</member>
<member name="M:Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSetSerializer.GetNextLinkGenerator(Microsoft.OData.ODataResourceSetBase,System.Collections.Generic.IAsyncEnumerable{System.Object},Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext)">
<summary>
Creates a function that takes in an object and generates a nextlink uri.
</summary>
<param name="resourceSet">The resource set describing a collection of structured objects.</param>
<param name="resourceSetInstance">The instance representing the resourceSet being written.</param>
<param name="writeContext">The serializer context.</param>
<returns>The function that generates the NextLink from an object.</returns>
</member>
<member name="M:Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSetSerializer.CreateODataOperation(Microsoft.OData.Edm.IEdmOperation,Microsoft.AspNetCore.OData.Formatter.ResourceSetContext,Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext)">
<summary>
Creates an <see cref="T:Microsoft.OData.ODataOperation" /> to be written for the given operation and the resourceSet instance.
Expand Down
1 change: 1 addition & 0 deletions src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1842,6 +1842,7 @@ virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializ
virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.CreateUntypedPropertyValue(Microsoft.OData.Edm.IEdmStructuralProperty structuralProperty, Microsoft.AspNetCore.OData.Formatter.ResourceContext resourceContext, out Microsoft.OData.Edm.IEdmTypeReference actualType) -> object
virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.WriteDeltaObjectInlineAsync(object graph, Microsoft.OData.Edm.IEdmTypeReference expectedType, Microsoft.OData.ODataWriter writer, Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext writeContext) -> System.Threading.Tasks.Task
virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSetSerializer.CreateODataOperation(Microsoft.OData.Edm.IEdmOperation operation, Microsoft.AspNetCore.OData.Formatter.ResourceSetContext resourceSetContext, Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext writeContext) -> Microsoft.OData.ODataOperation
virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSetSerializer.CreateResourceSet(System.Collections.Generic.IAsyncEnumerable<object> resourceSetInstance, Microsoft.OData.Edm.IEdmCollectionTypeReference resourceSetType, Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext writeContext) -> Microsoft.OData.ODataResourceSet
virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSetSerializer.CreateResourceSet(System.Collections.IEnumerable resourceSetInstance, Microsoft.OData.Edm.IEdmCollectionTypeReference resourceSetType, Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext writeContext) -> Microsoft.OData.ODataResourceSet
virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteEnumItemAsync(object enumValue, Microsoft.OData.Edm.IEdmTypeReference enumType, Microsoft.OData.Edm.IEdmTypeReference parentSetType, Microsoft.OData.ODataWriter writer, Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext writeContext) -> System.Threading.Tasks.Task
virtual Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSetSerializer.WritePrimitiveItemAsync(object primitiveValue, Microsoft.OData.Edm.IEdmTypeReference primitiveType, Microsoft.OData.Edm.IEdmTypeReference parentSetType, Microsoft.OData.ODataWriter writer, Microsoft.AspNetCore.OData.Formatter.Serialization.ODataSerializerContext writeContext) -> System.Threading.Tasks.Task
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
//-----------------------------------------------------------------------------
// <copyright file="IAsyncEnumerableController.cs" company=".NET Foundation">
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// See License.txt in the project root for license information.
// </copyright>
//------------------------------------------------------------------------------

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OData.Query;
using Microsoft.AspNetCore.OData.Routing.Controllers;

namespace Microsoft.AspNetCore.OData.E2E.Tests.IAsyncEnumerableTests
{
public class CustomersController : ODataController
{
private readonly IAsyncEnumerableContext _context;

public CustomersController(IAsyncEnumerableContext context)
{
context.Database.EnsureCreated();
_context = context;

if (!_context.Customers.Any())
{
Generate();
}
}

[EnableQuery]
[HttpGet("v1/Customers")]
public IAsyncEnumerable<Customer> CustomersData()
{
IAsyncEnumerable<Customer> customers = CreateCollectionAsync<Customer>();

return customers;
}

[EnableQuery]
[HttpGet("odata/Customers")]
public IAsyncEnumerable<Customer> Get()
{
return _context.Customers.AsAsyncEnumerable();
}

public async IAsyncEnumerable<Customer> CreateCollectionAsync<T>()
{
await Task.Delay(5);
// Yield the items one by one asynchronously
yield return new Customer
{
Id = 1,
Name = "Customer1",
Orders = new List<Order> {
new Order {
Name = "Order1",
Price = 25
},
new Order {
Name = "Order2",
Price = 75
}
},
Address = new Address
{
Name = "City1",
Street = "Street1"
}
};

await Task.Delay(5);

yield return new Customer
{
Id = 2,
Name = "Customer2",
Orders = new List<Order> {
new Order {
Name = "Order1",
Price = 35
},
new Order {
Name = "Order2",
Price = 65
}
},
Address = new Address
{
Name = "City2",
Street = "Street2"
}
};
}

public void Generate()
{
for (int i = 1; i <= 3; i++)
{
var customer = new Customer
{
Name = "Customer" + (i + 1) % 2,
Orders =
new List<Order> {
new Order {
Name = "Order" + 2*i,
Price = i * 25
},
new Order {
Name = "Order" + 2*i+1,
Price = i * 75
}
},
Address = new Address
{
Name = "City" + i % 2,
Street = "Street" + i % 2,
}
};

_context.Customers.Add(customer);
}

_context.SaveChanges();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//-----------------------------------------------------------------------------
// <copyright file="IAsyncEnumerableDataModel.cs" company=".NET Foundation">
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// See License.txt in the project root for license information.
// </copyright>
//------------------------------------------------------------------------------

using System.Collections.Generic;
ElizabethOkerio marked this conversation as resolved.
Show resolved Hide resolved
using Microsoft.EntityFrameworkCore;

namespace Microsoft.AspNetCore.OData.E2E.Tests.IAsyncEnumerableTests
{
public class IAsyncEnumerableContext : DbContext
{
public IAsyncEnumerableContext(DbContextOptions<IAsyncEnumerableContext> options)
: base(options)
{
}

public DbSet<Customer> Customers { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>().OwnsOne(c => c.Address).WithOwner();
}
}

public class Customer
{
public int Id { get; set; }

public string Name { get; set; }

public Address Address { get; set; }

public IList<Order> Orders { get; set; }
}

public class Order
{
public int Id { get; set; }

public string Name { get; set; }

public int Price { get; set; }
}

public class Address
{
public string Name { get; set; }

public string Street { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//-----------------------------------------------------------------------------
// <copyright file="IAsyncEnumerableEdmModel.cs" company=".NET Foundation">
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// See License.txt in the project root for license information.
// </copyright>
//------------------------------------------------------------------------------

using Microsoft.OData.Edm;
using Microsoft.OData.ModelBuilder;

namespace Microsoft.AspNetCore.OData.E2E.Tests.IAsyncEnumerableTests
{
public class IAsyncEnumerableEdmModel
{
public static IEdmModel GetEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Customer>("Customers");
builder.EntitySet<Order>("Orders");
IEdmModel model = builder.GetEdmModel();
return model;
}
}
}
Loading