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

Adds ActivatorUtilities.CreateFactory<T> API #77185

Merged
merged 3 commits into from
Oct 20, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Microsoft.Extensions.DependencyInjection
public static partial class ActivatorUtilities
{
public static Microsoft.Extensions.DependencyInjection.ObjectFactory CreateFactory([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type instanceType, System.Type[] argumentTypes) { throw null; }
public static Microsoft.Extensions.DependencyInjection.ObjectFactory<T> CreateFactory<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] T>(System.Type[] argumentTypes) { throw null; }
public static object CreateInstance(System.IServiceProvider provider, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type instanceType, params object[] parameters) { throw null; }
public static T CreateInstance<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] T>(System.IServiceProvider provider, params object[] parameters) { throw null; }
public static object GetServiceOrCreateInstance(System.IServiceProvider provider, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type) { throw null; }
Expand Down Expand Up @@ -53,6 +54,7 @@ public partial interface ISupportRequiredService
object GetRequiredService(System.Type serviceType);
}
public delegate object ObjectFactory(System.IServiceProvider serviceProvider, object?[]? arguments);
public delegate T ObjectFactory<T>(System.IServiceProvider serviceProvider, object?[]? arguments);
public partial class ServiceCollection : Microsoft.Extensions.DependencyInjection.IServiceCollection, System.Collections.Generic.ICollection<Microsoft.Extensions.DependencyInjection.ServiceDescriptor>, System.Collections.Generic.IEnumerable<Microsoft.Extensions.DependencyInjection.ServiceDescriptor>, System.Collections.Generic.IList<Microsoft.Extensions.DependencyInjection.ServiceDescriptor>, System.Collections.IEnumerable
{
public ServiceCollection() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,7 @@ public static ObjectFactory CreateFactory(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type instanceType,
Type[] argumentTypes)
{
FindApplicableConstructor(instanceType, argumentTypes, out ConstructorInfo constructor, out int?[] parameterMap);

ParameterExpression? provider = Expression.Parameter(typeof(IServiceProvider), "provider");
ParameterExpression? argumentArray = Expression.Parameter(typeof(object[]), "argumentArray");
Expression? factoryExpressionBody = BuildFactoryExpression(constructor, parameterMap, provider, argumentArray);
CreateFactoryInternal(instanceType, argumentTypes, out ParameterExpression provider, out ParameterExpression argumentArray, out Expression factoryExpressionBody);

var factoryLambda = Expression.Lambda<Func<IServiceProvider, object?[]?, object>>(
factoryExpressionBody, provider, argumentArray);
Expand All @@ -140,6 +136,40 @@ public static ObjectFactory CreateFactory(
return result.Invoke;
}

/// <summary>
/// Create a delegate that will instantiate a type with constructor arguments provided directly
/// and/or from an <see cref="IServiceProvider"/>.
/// </summary>
/// <typeparam name="T">The type to activate</typeparam>
/// <param name="argumentTypes">
/// The types of objects, in order, that will be passed to the returned function as its second parameter
/// </param>
/// <returns>
/// A factory that will instantiate type T using an <see cref="IServiceProvider"/>
/// and an argument array containing objects matching the types defined in argumentTypes
/// </returns>
public static ObjectFactory<T>
CreateFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(
Type[] argumentTypes)
{
CreateFactoryInternal(typeof(T), argumentTypes, out ParameterExpression provider, out ParameterExpression argumentArray, out Expression factoryExpressionBody);

var factoryLambda = Expression.Lambda<Func<IServiceProvider, object?[]?, T>>(
factoryExpressionBody, provider, argumentArray);

Func<IServiceProvider, object?[]?, T>? result = factoryLambda.Compile();
return result.Invoke;
}

private static void CreateFactoryInternal([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type instanceType, Type[] argumentTypes, out ParameterExpression provider, out ParameterExpression argumentArray, out Expression factoryExpressionBody)
{
FindApplicableConstructor(instanceType, argumentTypes, out ConstructorInfo constructor, out int?[] parameterMap);

provider = Expression.Parameter(typeof(IServiceProvider), "provider");
argumentArray = Expression.Parameter(typeof(object[]), "argumentArray");
factoryExpressionBody = BuildFactoryExpression(constructor, parameterMap, provider, argumentArray);
}

/// <summary>
/// Instantiate a type with constructor arguments provided directly and/or from an <see cref="IServiceProvider"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// The result of <see cref="ActivatorUtilities.CreateFactory{T}"/>. A delegate to specify a factory method to call to instantiate an instance of type `T`
/// </summary>
/// <typeparam name="T">The type of the instance being returned</typeparam>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> to get service arguments from.</param>
/// <param name="arguments">Additional constructor arguments.</param>
/// <returns>An instance of T</returns>
public delegate T ObjectFactory<T>(IServiceProvider serviceProvider, object?[]? arguments);
}
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,27 @@ public void CreateInstance_ClassWithABC_MultipleCtorsWithSameLength_ThrowsAmbigu
ActivatorUtilities.CreateInstance<ClassWithABC_MultipleCtorsWithSameLength>(provider));
Assert.Equal(message, exception.Message);
}

[Fact]
public void CreateFactory_CreatesFactoryMethod()
{
var factory1 = ActivatorUtilities.CreateFactory(typeof(ClassWithABCS), new Type[] { typeof(B) });
var factory2 = ActivatorUtilities.CreateFactory<ClassWithABCS>(new Type[] { typeof(B) });

var services = new ServiceCollection();
services.AddSingleton(new A());
services.AddSingleton(new C());
services.AddSingleton(new S());
using var provider = services.BuildServiceProvider();
object item1 = factory1(provider, new[] { new B() });
var item2 = factory2(provider, new[] { new B() });

Assert.IsType<ObjectFactory>(factory1);
Assert.IsType<ClassWithABCS>(item1);

Assert.IsType<ObjectFactory<ClassWithABCS>>(factory2);
Assert.IsType<ClassWithABCS>(item2);
}
}

internal class A { }
Expand Down