Skip to content
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
16 changes: 13 additions & 3 deletions extensions/Worker.Extensions.Shared/Reflection/ParameterBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

Expand All @@ -14,6 +15,12 @@ namespace Microsoft.Azure.Functions.Worker.Extensions
/// </summary>
internal static class ParameterBinder
{
/// <summary>
/// Read only property that contains all of the generic interfaces implemented by <see cref="System.Collections.Generic.List"/>.
/// </summary>
/// <remarks>This property calls ToList at the end to force the resolution of the LINQ methods that leverage deferred execution.</remarks>
private static readonly HashSet<Type> validListInterfaceTypes = new HashSet<Type>(typeof(List<>).GetInterfaces().Where(t => t.IsGenericType).Select(t => t.GetGenericTypeDefinition()));

private const BindingFlags DeclaredOnlyLookup = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;

/// <summary>
Expand Down Expand Up @@ -91,12 +98,15 @@ public static async Task BindCollectionAsync(IAsyncEnumerable<object> pageable,
}
}

/// <summary>
/// A method that determines if a Type is a generic interface of the <see cref="System.Collections.Generic.List"/> concrete class.
/// </summary>
/// <param name="type">A generic interface type to be tested against the types available on the generic <see cref="System.Collections.Generic.List"/> type.</param>
/// <returns>True if the type is a generic type and it's an interface of the generic <see cref="System.Collections.Generic.List"/> type otherwise false.</returns>
private static bool IsListInterface(Type type)
{
return type.IsGenericType &&
(type.GetGenericTypeDefinition() == typeof(IList<>)
|| type.GetGenericTypeDefinition() == typeof(ICollection<>)
|| type.GetGenericTypeDefinition() == typeof(IEnumerable<>));
validListInterfaceTypes.Contains(type.GetGenericTypeDefinition());
}

private static Action<object> GetAddMethod(object collection)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,23 @@ public Task BindCollection_ThrowsOnUnsupportedType(Type type)
() => ParameterBinder.BindCollectionAsync(e => Mock.Of<IAsyncEnumerable<object>>(), type));
}

[Fact]
public async Task BindCollection_Interface_AllListInterfacesSupported()
{
var interfaceTypes = new List<int>().GetType().GetInterfaces().Where(t => t.IsGenericType);

foreach(Type interfaceType in interfaceTypes)
{
Assert.IsType<List<int>>(await ParameterBinder.BindCollectionAsync(GetIntEnumerable, interfaceType));
}
}

[Theory]
[InlineData(typeof(IEnumerable<int>))]
[InlineData(typeof(ICollection<int>))]
[InlineData(typeof(IList<int>))]
[InlineData(typeof(IReadOnlyList<int>))]
[InlineData(typeof(IReadOnlyCollection<int>))]
public async Task BindCollection_Interface_GetsList(Type type)
{
object collection = await ParameterBinder.BindCollectionAsync(GetIntEnumerable, type);
Expand Down