Skip to content

Commit

Permalink
support IAsyncenumerable
Browse files Browse the repository at this point in the history
  • Loading branch information
ElizabethOkerio committed Aug 8, 2023
1 parent e8f1e24 commit 99139b7
Show file tree
Hide file tree
Showing 12 changed files with 595 additions and 72 deletions.
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)
{
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>
/// <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;
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

0 comments on commit 99139b7

Please sign in to comment.