From a80c3751b146783fa56cc4b4539e7b59811914df Mon Sep 17 00:00:00 2001 From: Matthias Vill Date: Mon, 26 Nov 2018 20:32:04 +0100 Subject: [PATCH] Don't immediately throw in ConstructorInjectionConvention rather let the ModelFactory throw as the convention is applied twice in hosting case --- .../ConstructorInjectionConvention.cs | 56 ++++++++----------- .../ConstructorInjectionConventionTests.cs | 6 +- 2 files changed, 26 insertions(+), 36 deletions(-) diff --git a/src/CommandLineUtils/Conventions/ConstructorInjectionConvention.cs b/src/CommandLineUtils/Conventions/ConstructorInjectionConvention.cs index b8b46dec..adefa4e7 100644 --- a/src/CommandLineUtils/Conventions/ConstructorInjectionConvention.cs +++ b/src/CommandLineUtils/Conventions/ConstructorInjectionConvention.cs @@ -44,11 +44,11 @@ public virtual void Apply(ConventionContext context) return; } - s_applyMethod.MakeGenericMethod(context.ModelType).Invoke(this, new object[] { context }); + s_applyMethod.MakeGenericMethod(context.ModelType).Invoke(this, new object[] {context}); } private static readonly MethodInfo s_applyMethod - = typeof(ConstructorInjectionConvention).GetRuntimeMethods().Single(m => m.Name == nameof(ApplyImpl)); + = typeof(ConstructorInjectionConvention).GetRuntimeMethods().Single(m => m.Name == nameof(ApplyImpl)); private void ApplyImpl(ConventionContext context) where TModel : class @@ -62,34 +62,16 @@ private void ApplyImpl(ConventionContext context) throw new InvalidOperationException(Strings.NoAnyPublicConstuctorFound(typeof(TModel))); } - var (matchedCtor, matchedArgs) = (constructors.Length == 1) - // shortcut for single constructor - ? FindMatchedConstructor(constructors, context.Application, - throwIfNoParameterTypeRegistered: true) - // find the constructor with the most parameters first - : FindMatchedConstructor(constructors, context.Application, - throwIfNoParameterTypeRegistered: false); + var factory = FindMatchedConstructor(constructors, context.Application, + constructors.Length == 1); - var parameterLessCtor = Array.Find(constructors, c => c.GetParameters().Length == 0); - - if (matchedCtor == null && parameterLessCtor != null) - { - return; - } - - if (matchedCtor == null && parameterLessCtor == null) + if (factory != null) { - throw new InvalidOperationException(Strings.NoMatchedConstructorFound(typeof(TModel))); - } - - if (matchedCtor != null) - { - (context.Application as CommandLineApplication).ModelFactory = - () => (TModel)matchedCtor.Invoke(matchedArgs); + ((CommandLineApplication) context.Application).ModelFactory = factory; } } - private (ConstructorInfo matchedCtor, object[] matchedArgs) FindMatchedConstructor( + private static Func FindMatchedConstructor( ConstructorInfo[] constructors, IServiceProvider services, bool throwIfNoParameterTypeRegistered = false) @@ -97,6 +79,11 @@ private void ApplyImpl(ConventionContext context) foreach (var ctorCandidate in constructors.OrderByDescending(c => c.GetParameters().Length)) { var parameters = ctorCandidate.GetParameters().ToArray(); + if (parameters.Length == 0) + { + return null; + } + var args = new object[parameters.Length]; for (var i = 0; i < parameters.Length; i++) { @@ -106,22 +93,25 @@ private void ApplyImpl(ConventionContext context) { if (!throwIfNoParameterTypeRegistered) { - continue; + goto nextCtor; } - throw new InvalidOperationException(Strings.NoParameterTypeRegistered(ctorCandidate.DeclaringType, paramType)); + + return () => + throw new InvalidOperationException( + Strings.NoParameterTypeRegistered(ctorCandidate.DeclaringType, paramType)); } + args[i] = service; if (i == parameters.Length - 1) { - var matchedArgsLength = args.Count(x => x != null); - if (parameters.Length == matchedArgsLength) - { - return (ctorCandidate, args); - } + return () => (TModel) ctorCandidate.Invoke(args); } } + + nextCtor: ; } - return (null, null); + + return () => throw new InvalidOperationException(Strings.NoMatchedConstructorFound(typeof(TModel))); } } } diff --git a/test/CommandLineUtils.Tests/ConstructorInjectionConventionTests.cs b/test/CommandLineUtils.Tests/ConstructorInjectionConventionTests.cs index 098aba8c..c909fa5e 100644 --- a/test/CommandLineUtils.Tests/ConstructorInjectionConventionTests.cs +++ b/test/CommandLineUtils.Tests/ConstructorInjectionConventionTests.cs @@ -149,9 +149,9 @@ public TestAppWithoutMatchedConstructor(TestConsole console) public void ThrowsWhenNoMatchedConstructorFound() { var app = new CommandLineApplication(); - var ex = Assert.Throws(() => app.Conventions.UseConstructorInjection()); - Assert.IsType(ex.InnerException); - Assert.Equal(Strings.NoParameterTypeRegistered(typeof(TestAppWithoutMatchedConstructor), typeof(TestConsole)), ex.InnerException.Message); + app.Conventions.UseConstructorInjection(); + var ex = Assert.Throws(() => app.ModelFactory()); + Assert.Equal(Strings.NoParameterTypeRegistered(typeof(TestAppWithoutMatchedConstructor), typeof(TestConsole)), ex.Message); } private class TestAppWithAlternativeConstructor