@@ -31,18 +31,35 @@ public static object CreateInstance(
3131 [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors ) ] Type instanceType ,
3232 params object [ ] parameters )
3333 {
34- int bestLength = - 1 ;
35- bool seenPreferred = false ;
34+ if ( provider == null )
35+ {
36+ throw new ArgumentNullException ( nameof ( provider ) ) ;
37+ }
3638
37- ConstructorMatcher bestMatcher = default ;
39+ if ( instanceType . IsAbstract )
40+ {
41+ throw new InvalidOperationException ( SR . CannotCreateAbstractClasses ) ;
42+ }
3843
39- if ( ! instanceType . IsAbstract )
44+ IServiceProviderIsService ? serviceProviderIsService = provider . GetService < IServiceProviderIsService > ( ) ;
45+ // if container supports using IServiceProviderIsService, we try to find the longest ctor that
46+ // (a) matches all parameters given to CreateInstance
47+ // (b) matches the rest of ctor arguments as either a parameter with a default value or as a service registered
48+ // if no such match is found we fallback to the same logic used by CreateFactory which would only allow creating an
49+ // instance if all parameters given to CreateInstance only match with a single ctor
50+ if ( serviceProviderIsService != null )
4051 {
52+ int bestLength = - 1 ;
53+ bool seenPreferred = false ;
54+
55+ ConstructorMatcher bestMatcher = default ;
56+ bool multipleBestLengthFound = false ;
57+
4158 foreach ( ConstructorInfo ? constructor in instanceType . GetConstructors ( ) )
4259 {
4360 var matcher = new ConstructorMatcher ( constructor ) ;
4461 bool isPreferred = constructor . IsDefined ( typeof ( ActivatorUtilitiesConstructorAttribute ) , false ) ;
45- int length = matcher . Match ( parameters ) ;
62+ int length = matcher . Match ( parameters , serviceProviderIsService ) ;
4663
4764 if ( isPreferred )
4865 {
@@ -61,19 +78,37 @@ public static object CreateInstance(
6178 {
6279 bestLength = length ;
6380 bestMatcher = matcher ;
81+ multipleBestLengthFound = false ;
82+ }
83+ else if ( bestLength == length )
84+ {
85+ multipleBestLengthFound = true ;
6486 }
6587
6688 seenPreferred |= isPreferred ;
6789 }
90+
91+ if ( bestLength != - 1 )
92+ {
93+ if ( multipleBestLengthFound )
94+ {
95+ throw new InvalidOperationException ( SR . Format ( SR . MultipleCtorsFoundWithBestLength , instanceType , bestLength ) ) ;
96+ }
97+
98+ return bestMatcher . CreateInstance ( provider ) ;
99+ }
68100 }
69101
70- if ( bestLength == - 1 )
102+ Type ? [ ] argumentTypes = new Type [ parameters . Length ] ;
103+ for ( int i = 0 ; i < argumentTypes . Length ; i ++ )
71104 {
72- string ? message = $ "A suitable constructor for type '{ instanceType } ' could not be located. Ensure the type is concrete and all parameters of a public constructor are either registered as services or passed as arguments. Also ensure no extraneous arguments are provided.";
73- throw new InvalidOperationException ( message ) ;
105+ argumentTypes [ i ] = parameters [ i ] ? . GetType ( ) ;
74106 }
75107
76- return bestMatcher . CreateInstance ( provider ) ;
108+ FindApplicableConstructor ( instanceType , argumentTypes , out ConstructorInfo constructorInfo , out int ? [ ] parameterMap ) ;
109+ var constructorMatcher = new ConstructorMatcher ( constructorInfo ) ;
110+ constructorMatcher . MapParameters ( parameterMap , parameters ) ;
111+ return constructorMatcher . CreateInstance ( provider ) ;
77112 }
78113
79114 /// <summary>
@@ -92,7 +127,7 @@ public static ObjectFactory CreateFactory(
92127 [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors ) ] Type instanceType ,
93128 Type [ ] argumentTypes )
94129 {
95- FindApplicableConstructor ( instanceType , argumentTypes , out ConstructorInfo ? constructor , out int ? [ ] ? parameterMap ) ;
130+ FindApplicableConstructor ( instanceType , argumentTypes , out ConstructorInfo constructor , out int ? [ ] parameterMap ) ;
96131
97132 ParameterExpression ? provider = Expression . Parameter ( typeof ( IServiceProvider ) , "provider" ) ;
98133 ParameterExpression ? argumentArray = Expression . Parameter ( typeof ( object [ ] ) , "argumentArray" ) ;
@@ -152,8 +187,7 @@ private static MethodInfo GetMethodInfo<T>(Expression<T> expr)
152187 object ? service = sp . GetService ( type ) ;
153188 if ( service == null && ! isDefaultParameterRequired )
154189 {
155- string ? message = $ "Unable to resolve service for type '{ type } ' while attempting to activate '{ requiredBy } '.";
156- throw new InvalidOperationException ( message ) ;
190+ throw new InvalidOperationException ( SR . Format ( SR . UnableToResolveService , type , requiredBy ) ) ;
157191 }
158192 return service ;
159193 }
@@ -202,7 +236,7 @@ private static Expression BuildFactoryExpression(
202236
203237 private static void FindApplicableConstructor (
204238 [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors ) ] Type instanceType ,
205- Type [ ] argumentTypes ,
239+ Type ? [ ] argumentTypes ,
206240 out ConstructorInfo matchingConstructor ,
207241 out int ? [ ] matchingParameterMap )
208242 {
@@ -212,8 +246,7 @@ private static void FindApplicableConstructor(
212246 if ( ! TryFindPreferredConstructor ( instanceType , argumentTypes , ref constructorInfo , ref parameterMap ) &&
213247 ! TryFindMatchingConstructor ( instanceType , argumentTypes , ref constructorInfo , ref parameterMap ) )
214248 {
215- string ? message = $ "A suitable constructor for type '{ instanceType } ' could not be located. Ensure the type is concrete and all parameters of a public constructor are either registered as services or passed as arguments. Also ensure no extraneous arguments are provided.";
216- throw new InvalidOperationException ( message ) ;
249+ throw new InvalidOperationException ( SR . Format ( SR . CtorNotLocated , instanceType ) ) ;
217250 }
218251
219252 matchingConstructor = constructorInfo ;
@@ -223,7 +256,7 @@ private static void FindApplicableConstructor(
223256 // Tries to find constructor based on provided argument types
224257 private static bool TryFindMatchingConstructor (
225258 [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors ) ] Type instanceType ,
226- Type [ ] argumentTypes ,
259+ Type ? [ ] argumentTypes ,
227260 [ NotNullWhen ( true ) ] ref ConstructorInfo ? matchingConstructor ,
228261 [ NotNullWhen ( true ) ] ref int ? [ ] ? parameterMap )
229262 {
@@ -233,7 +266,7 @@ private static bool TryFindMatchingConstructor(
233266 {
234267 if ( matchingConstructor != null )
235268 {
236- throw new InvalidOperationException ( $ "Multiple constructors accepting all given argument types have been found in type ' { instanceType } '. There should only be one applicable constructor." ) ;
269+ throw new InvalidOperationException ( SR . Format ( SR . MultipleCtorsFound , instanceType ) ) ;
237270 }
238271
239272 matchingConstructor = constructor ;
@@ -253,7 +286,7 @@ private static bool TryFindMatchingConstructor(
253286 // Tries to find constructor marked with ActivatorUtilitiesConstructorAttribute
254287 private static bool TryFindPreferredConstructor (
255288 [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors ) ] Type instanceType ,
256- Type [ ] argumentTypes ,
289+ Type ? [ ] argumentTypes ,
257290 [ NotNullWhen ( true ) ] ref ConstructorInfo ? matchingConstructor ,
258291 [ NotNullWhen ( true ) ] ref int ? [ ] ? parameterMap )
259292 {
@@ -289,7 +322,7 @@ private static bool TryFindPreferredConstructor(
289322
290323 // Creates an injective parameterMap from givenParameterTypes to assignable constructorParameters.
291324 // Returns true if each given parameter type is assignable to a unique; otherwise, false.
292- private static bool TryCreateParameterMap ( ParameterInfo [ ] constructorParameters , Type [ ] argumentTypes , out int ? [ ] parameterMap )
325+ private static bool TryCreateParameterMap ( ParameterInfo [ ] constructorParameters , Type ? [ ] argumentTypes , out int ? [ ] parameterMap )
293326 {
294327 parameterMap = new int ? [ constructorParameters . Length ] ;
295328
@@ -336,39 +369,48 @@ public ConstructorMatcher(ConstructorInfo constructor)
336369 _parameterValues = new object ? [ _parameters . Length ] ;
337370 }
338371
339- public int Match ( object [ ] givenParameters )
372+ public int Match ( object [ ] givenParameters , IServiceProviderIsService serviceProviderIsService )
340373 {
341- int applyIndexStart = 0 ;
342- int applyExactLength = 0 ;
343- for ( int givenIndex = 0 ; givenIndex != givenParameters . Length ; givenIndex ++ )
374+ for ( int givenIndex = 0 ; givenIndex < givenParameters . Length ; givenIndex ++ )
344375 {
345376 Type ? givenType = givenParameters [ givenIndex ] ? . GetType ( ) ;
346377 bool givenMatched = false ;
347378
348- for ( int applyIndex = applyIndexStart ; givenMatched == false && applyIndex != _parameters . Length ; ++ applyIndex )
379+ for ( int applyIndex = 0 ; applyIndex < _parameters . Length ; applyIndex ++ )
349380 {
350381 if ( _parameterValues [ applyIndex ] == null &&
351382 _parameters [ applyIndex ] . ParameterType . IsAssignableFrom ( givenType ) )
352383 {
353384 givenMatched = true ;
354385 _parameterValues [ applyIndex ] = givenParameters [ givenIndex ] ;
355- if ( applyIndexStart == applyIndex )
356- {
357- applyIndexStart ++ ;
358- if ( applyIndex == givenIndex )
359- {
360- applyExactLength = applyIndex ;
361- }
362- }
386+ break ;
363387 }
364388 }
365389
366- if ( givenMatched == false )
390+ if ( ! givenMatched )
367391 {
368392 return - 1 ;
369393 }
370394 }
371- return applyExactLength ;
395+
396+ // confirms the rest of ctor arguments match either as a parameter with a default value or as a service registered
397+ for ( int i = 0 ; i < _parameters . Length ; i ++ )
398+ {
399+ if ( _parameterValues [ i ] == null &&
400+ ! serviceProviderIsService . IsService ( _parameters [ i ] . ParameterType ) )
401+ {
402+ if ( ParameterDefaultValue . TryGetDefaultValue ( _parameters [ i ] , out object ? defaultValue ) )
403+ {
404+ _parameterValues [ i ] = defaultValue ;
405+ }
406+ else
407+ {
408+ return - 1 ;
409+ }
410+ }
411+ }
412+
413+ return _parameters . Length ;
372414 }
373415
374416 public object CreateInstance ( IServiceProvider provider )
@@ -382,7 +424,7 @@ public object CreateInstance(IServiceProvider provider)
382424 {
383425 if ( ! ParameterDefaultValue . TryGetDefaultValue ( _parameters [ index ] , out object ? defaultValue ) )
384426 {
385- throw new InvalidOperationException ( $ "Unable to resolve service for type ' { _parameters [ index ] . ParameterType } ' while attempting to activate ' { _constructor . DeclaringType } '." ) ;
427+ throw new InvalidOperationException ( SR . Format ( SR . UnableToResolveService , _parameters [ index ] . ParameterType , _constructor . DeclaringType ) ) ;
386428 }
387429 else
388430 {
@@ -411,16 +453,27 @@ public object CreateInstance(IServiceProvider provider)
411453 return _constructor . Invoke ( BindingFlags . DoNotWrapExceptions , binder : null , parameters : _parameterValues , culture : null ) ;
412454#endif
413455 }
456+
457+ public void MapParameters ( int ? [ ] parameterMap , object [ ] givenParameters )
458+ {
459+ for ( int i = 0 ; i < _parameters . Length ; i ++ )
460+ {
461+ if ( parameterMap [ i ] != null )
462+ {
463+ _parameterValues [ i ] = givenParameters [ ( int ) parameterMap [ i ] ! ] ;
464+ }
465+ }
466+ }
414467 }
415468
416469 private static void ThrowMultipleCtorsMarkedWithAttributeException ( )
417470 {
418- throw new InvalidOperationException ( $ "Multiple constructors were marked with { nameof ( ActivatorUtilitiesConstructorAttribute ) } ." ) ;
471+ throw new InvalidOperationException ( SR . Format ( SR . MultipleCtorsMarkedWithAttribute , nameof ( ActivatorUtilitiesConstructorAttribute ) ) ) ;
419472 }
420473
421474 private static void ThrowMarkedCtorDoesNotTakeAllProvidedArguments ( )
422475 {
423- throw new InvalidOperationException ( $ "Constructor marked with { nameof ( ActivatorUtilitiesConstructorAttribute ) } does not accept all given argument types." ) ;
476+ throw new InvalidOperationException ( SR . Format ( SR . MarkedCtorMissingArgumentTypes , nameof ( ActivatorUtilitiesConstructorAttribute ) ) ) ;
424477 }
425478 }
426479}
0 commit comments