From e542a2081d8191dc9bf0b7a83475a57e385e96ac Mon Sep 17 00:00:00 2001 From: Maryam Ariyan Date: Thu, 20 Oct 2022 16:37:34 -0400 Subject: [PATCH] Adds `ActivatorUtilities.CreateFactory` API (#77185) --- ...nsions.DependencyInjection.Abstractions.cs | 2 + .../src/ActivatorUtilities.cs | 40 ++++++++++++++++--- .../src/ObjectFactoryT.cs | 16 ++++++++ .../tests/DI.Tests/ActivatorUtilitiesTests.cs | 21 ++++++++++ 4 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ObjectFactoryT.cs diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/ref/Microsoft.Extensions.DependencyInjection.Abstractions.cs b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/ref/Microsoft.Extensions.DependencyInjection.Abstractions.cs index 0d7359b076e6d..abb9b58cdbde2 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/ref/Microsoft.Extensions.DependencyInjection.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/ref/Microsoft.Extensions.DependencyInjection.Abstractions.cs @@ -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 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; } @@ -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(System.IServiceProvider serviceProvider, object?[]? arguments); public partial class ServiceCollection : Microsoft.Extensions.DependencyInjection.IServiceCollection, System.Collections.Generic.ICollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.IList, System.Collections.IEnumerable { public ServiceCollection() { } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs index 62727442fc149..0a2cb5ba943f4 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs @@ -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>( factoryExpressionBody, provider, argumentArray); @@ -140,6 +136,40 @@ public static ObjectFactory CreateFactory( return result.Invoke; } + /// + /// Create a delegate that will instantiate a type with constructor arguments provided directly + /// and/or from an . + /// + /// The type to activate + /// + /// The types of objects, in order, that will be passed to the returned function as its second parameter + /// + /// + /// A factory that will instantiate type T using an + /// and an argument array containing objects matching the types defined in argumentTypes + /// + public static ObjectFactory + CreateFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>( + Type[] argumentTypes) + { + CreateFactoryInternal(typeof(T), argumentTypes, out ParameterExpression provider, out ParameterExpression argumentArray, out Expression factoryExpressionBody); + + var factoryLambda = Expression.Lambda>( + factoryExpressionBody, provider, argumentArray); + + Func? 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); + } + /// /// Instantiate a type with constructor arguments provided directly and/or from an . /// diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ObjectFactoryT.cs b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ObjectFactoryT.cs new file mode 100644 index 0000000000000..52ec33f8dc7cf --- /dev/null +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ObjectFactoryT.cs @@ -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 +{ + /// + /// The result of . A delegate to specify a factory method to call to instantiate an instance of type `T` + /// + /// The type of the instance being returned + /// The to get service arguments from. + /// Additional constructor arguments. + /// An instance of T + public delegate T ObjectFactory(IServiceProvider serviceProvider, object?[]? arguments); +} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ActivatorUtilitiesTests.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ActivatorUtilitiesTests.cs index 3ca6e77663caa..47af6f2558de3 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ActivatorUtilitiesTests.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ActivatorUtilitiesTests.cs @@ -170,6 +170,27 @@ public void CreateInstance_ClassWithABC_MultipleCtorsWithSameLength_ThrowsAmbigu ActivatorUtilities.CreateInstance(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(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(factory1); + Assert.IsType(item1); + + Assert.IsType>(factory2); + Assert.IsType(item2); + } } internal class A { }