From 17b83afd44460d91742e4f434715a43015baac3b Mon Sep 17 00:00:00 2001 From: Ivan Appert Date: Fri, 25 Apr 2014 11:24:34 +0200 Subject: [PATCH] Don't score constructors if there is just one. --- src/Ninject.Test/ExtensionsForIEnumerable.cs | 4 +- .../Activation/Providers/StandardProvider.cs | 127 +++++++++++------- .../Language/ExtensionsForIEnumerableOfT.cs | 74 +++++++--- src/Ninject/Planning/IPlan.cs | 5 + src/Ninject/Planning/Plan.cs | 66 ++++++--- src/Ninject/Settings.StyleCop | 5 + 6 files changed, 190 insertions(+), 91 deletions(-) diff --git a/src/Ninject.Test/ExtensionsForIEnumerable.cs b/src/Ninject.Test/ExtensionsForIEnumerable.cs index bbfd2ee3..9f3279ac 100644 --- a/src/Ninject.Test/ExtensionsForIEnumerable.cs +++ b/src/Ninject.Test/ExtensionsForIEnumerable.cs @@ -15,8 +15,8 @@ public static void Map(this IEnumerable series, Action action) } public static T ShouldContainSingle(this IEnumerable source) - { + { return Assert.Single(source); - } + } } } \ No newline at end of file diff --git a/src/Ninject/Activation/Providers/StandardProvider.cs b/src/Ninject/Activation/Providers/StandardProvider.cs index ea0f1024..81de6487 100644 --- a/src/Ninject/Activation/Providers/StandardProvider.cs +++ b/src/Ninject/Activation/Providers/StandardProvider.cs @@ -1,28 +1,34 @@ -#region License -// -// Author: Nate Kohari -// Copyright (c) 2007-2010, Enkari, Ltd. -// -// Dual-licensed under the Apache License, Version 2.0, and the Microsoft Public License (Ms-PL). -// See the file LICENSE.txt for details. -// -#endregion -#region Using Directives -using System; -using System.Linq; - -using Ninject.Infrastructure.Introspection; -using Ninject.Parameters; -using Ninject.Planning.Directives; -using Ninject.Planning.Targets; - -#endregion +//------------------------------------------------------------------------------- +// +// Copyright (c) 2009-2014 Ninject Project Contributors +// +// Dual-licensed under the Apache License, Version 2.0, and the Microsoft Public License (Ms-PL). +// You may not use this file except in compliance with one of the Licenses. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// or +// http://www.microsoft.com/opensource/licenses.mspx +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//------------------------------------------------------------------------------- namespace Ninject.Activation.Providers { + using System; + using System.Linq; using System.Reflection; - + using Ninject.Infrastructure.Introspection; + using Ninject.Infrastructure.Language; + using Ninject.Parameters; using Ninject.Planning.Bindings; + using Ninject.Planning.Directives; + using Ninject.Planning.Targets; using Ninject.Selection; using Ninject.Selection.Heuristics; @@ -31,16 +37,6 @@ namespace Ninject.Activation.Providers /// public class StandardProvider : IProvider { - /// - /// Gets the type (or prototype) of instances the provider creates. - /// - public Type Type { get; private set; } - - /// - /// Gets or sets the selector component. - /// - public IConstructorScorer ConstructorScorer { get; private set; } - /// /// Initializes a new instance of the class. /// @@ -48,10 +44,20 @@ public class StandardProvider : IProvider /// The constructor scorer component. public StandardProvider(Type type, IConstructorScorer constructorScorer) { - Type = type; - ConstructorScorer = constructorScorer; + this.Type = type; + this.ConstructorScorer = constructorScorer; } + /// + /// Gets the type (or prototype) of instances the provider creates. + /// + public Type Type { get; private set; } + + /// + /// Gets the selector component. + /// + public IConstructorScorer ConstructorScorer { get; private set; } + /// /// Creates an instance within the specified context. /// @@ -60,23 +66,11 @@ public StandardProvider(Type type, IConstructorScorer constructorScorer) public virtual object Create(IContext context) { context.BuildPlan(this.GetImplementationType(context.Request.Service)); - if (!context.Plan.Has()) - { - throw new ActivationException(ExceptionFormatter.NoConstructorsAvailable(context)); - } - var directives = context.Plan.GetAll(); - var bestDirectives = directives - .GroupBy(option => this.ConstructorScorer.Score(context, option)) - .OrderByDescending(g => g.Key) - .First(); - if (bestDirectives.Skip(1).Any()) - { - throw new ActivationException(ExceptionFormatter.ConstructorsAmbiguous(context, bestDirectives)); - } + var directive = this.DetermineConstructorInjectionDirective(context); - var directive = bestDirectives.Single(); var arguments = directive.Targets.Select(target => this.GetValue(context, target)).ToArray(); + return directive.Injector(arguments); } @@ -109,9 +103,15 @@ public Type GetImplementationType(Type service) /// Gets a callback that creates an instance of the /// for the specified type. /// - /// The prototype the provider instance will create. - /// The selector - /// The created callback. + /// + /// The prototype the provider instance will create. + /// + /// + /// The selector. + /// + /// + /// The created callback. + /// public static Func GetCreationCallback(Type prototype, ISelector selector) { var provider = new StandardProvider(prototype, selector.ConstructorScorer); @@ -134,8 +134,12 @@ public static Func GetCreationCallback(Type prototype, Cons /// /// Assigns the provider callback to the building configuration. /// - /// The building configuration - /// The prototype + /// + /// The building configuration. + /// + /// + /// The prototype. + /// public static void AssignProviderCallback(IBindingConfiguration bindingConfiguration, Type prototype) { var provider = new StandardProvider(prototype, null); @@ -143,5 +147,26 @@ public static void AssignProviderCallback(IBindingConfiguration bindingConfigura bindingConfiguration.InitializeProviderCallback = selector => provider.ConstructorScorer = selector.ConstructorScorer; } + + private ConstructorInjectionDirective DetermineConstructorInjectionDirective(IContext context) + { + var directives = context.Plan.ConstructorInjectionDirectives; + if (directives.Count == 1) + { + return directives[0]; + } + IGrouping bestDirectives = + directives + .GroupBy(option => this.ConstructorScorer.Score(context, option)) + .OrderByDescending(g => g.Key) + .FirstOrDefault(); + if (bestDirectives == null) + { + throw new ActivationException(ExceptionFormatter.NoConstructorsAvailable(context)); + } + + return bestDirectives.SingleOrThrowException( + () => new ActivationException(ExceptionFormatter.ConstructorsAmbiguous(context, bestDirectives))); + } } } \ No newline at end of file diff --git a/src/Ninject/Infrastructure/Language/ExtensionsForIEnumerableOfT.cs b/src/Ninject/Infrastructure/Language/ExtensionsForIEnumerableOfT.cs index 527adc6a..f1b6d93b 100644 --- a/src/Ninject/Infrastructure/Language/ExtensionsForIEnumerableOfT.cs +++ b/src/Ninject/Infrastructure/Language/ExtensionsForIEnumerableOfT.cs @@ -1,35 +1,46 @@ -#region License -// -// Author: Nate Kohari -// Copyright (c) 2007-2010, Enkari, Ltd. -// -// Dual-licensed under the Apache License, Version 2.0, and the Microsoft Public License (Ms-PL). -// See the file LICENSE.txt for details. -// -#endregion -#region Using Directives -using System; -using System.Collections.Generic; -using System.Linq; -#endregion +//------------------------------------------------------------------------------- +// +// Copyright (c) 2009-2014 Ninject Project Contributors +// +// Dual-licensed under the Apache License, Version 2.0, and the Microsoft Public License (Ms-PL). +// You may not use this file except in compliance with one of the Licenses. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// or +// http://www.microsoft.com/opensource/licenses.mspx +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//------------------------------------------------------------------------------- namespace Ninject.Infrastructure.Language { + using System; + using System.Collections.Generic; + using System.Linq; + /// - /// Provides extension methods for see cref="IEnumerable{T}"/> + /// Provides extension methods for see IEnumerable. /// public static class ExtensionsForIEnumerableOfT { /// /// Executes the given action for each of the elements in the enumerable. /// - /// + /// Type of the enumerable. /// The series. /// The action. public static void Map(this IEnumerable series, Action action) { foreach (T item in series) + { action(item); + } } /// @@ -42,5 +53,36 @@ public static IEnumerable ToEnumerable(this IEnumerable series) { return series.Select(x => x); } + + /// + /// Returns single element of enumerable or throws exception. + /// + /// + /// The enumerable. + /// + /// + /// The exception creator. + /// + /// + /// Type of IEnumerable. + /// + /// + /// Single element of enumerable. + /// + /// + /// Exception specified by exception creator. + /// + public static T SingleOrThrowException(this IEnumerable series, Func exceptionCreator) + { + var e = series.GetEnumerator(); + e.MoveNext(); + var result = e.Current; + if (e.MoveNext()) + { + throw exceptionCreator(); + } + + return result; + } } } \ No newline at end of file diff --git a/src/Ninject/Planning/IPlan.cs b/src/Ninject/Planning/IPlan.cs index 19c7dcfd..197322f7 100644 --- a/src/Ninject/Planning/IPlan.cs +++ b/src/Ninject/Planning/IPlan.cs @@ -25,6 +25,11 @@ public interface IPlan /// Type Type { get; } + /// + /// Gets the directives defined in the plan. + /// + IList ConstructorInjectionDirectives { get; } + /// /// Adds the specified directive to the plan. /// diff --git a/src/Ninject/Planning/Plan.cs b/src/Ninject/Planning/Plan.cs index 9ae3e5bd..440b58b1 100644 --- a/src/Ninject/Planning/Plan.cs +++ b/src/Ninject/Planning/Plan.cs @@ -1,22 +1,30 @@ -#region License -// -// Author: Nate Kohari -// Copyright (c) 2007-2010, Enkari, Ltd. -// -// Dual-licensed under the Apache License, Version 2.0, and the Microsoft Public License (Ms-PL). -// See the file LICENSE.txt for details. -// -#endregion -#region Using Directives -using System; -using System.Collections.Generic; -using System.Linq; -using Ninject.Infrastructure; -using Ninject.Planning.Directives; -#endregion +//------------------------------------------------------------------------------- +// +// Copyright (c) 2009-2014 Ninject Project Contributors +// +// Dual-licensed under the Apache License, Version 2.0, and the Microsoft Public License (Ms-PL). +// You may not use this file except in compliance with one of the Licenses. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// or +// http://www.microsoft.com/opensource/licenses.mspx +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//------------------------------------------------------------------------------- namespace Ninject.Planning { + using System; + using System.Collections.Generic; + using System.Linq; + using Ninject.Planning.Directives; + /// /// Describes the means by which a type should be activated. /// @@ -32,6 +40,11 @@ public class Plan : IPlan /// public ICollection Directives { get; private set; } + /// + /// Gets the constructor injection directives defined in the plan. + /// + public IList ConstructorInjectionDirectives { get; private set; } + /// /// Initializes a new instance of the class. /// @@ -39,7 +52,8 @@ public class Plan : IPlan public Plan(Type type) { Type = type; - Directives = new List(); + this.Directives = new List(); + this.ConstructorInjectionDirectives = new List(); } /// @@ -48,8 +62,16 @@ public Plan(Type type) /// The directive. public void Add(IDirective directive) { - Directives.Add(directive); - } + var constructorInjectionDirective = directive as ConstructorInjectionDirective; + if (constructorInjectionDirective != null) + { + this.ConstructorInjectionDirectives.Add(constructorInjectionDirective); + } + else + { + this.Directives.Add(directive); + } + } /// /// Determines whether the plan contains one or more directives of the specified type. @@ -59,7 +81,7 @@ public void Add(IDirective directive) public bool Has() where TDirective : IDirective { - return GetAll().Count() > 0; + return this.GetAll().Any(); } /// @@ -70,7 +92,7 @@ public bool Has() public TDirective GetOne() where TDirective : IDirective { - return GetAll().SingleOrDefault(); + return this.GetAll().SingleOrDefault(); } /// @@ -81,7 +103,7 @@ public TDirective GetOne() public IEnumerable GetAll() where TDirective : IDirective { - return Directives.OfType(); + return this.Directives.OfType(); } } } \ No newline at end of file diff --git a/src/Ninject/Settings.StyleCop b/src/Ninject/Settings.StyleCop index 4c262dd2..e5888388 100644 --- a/src/Ninject/Settings.StyleCop +++ b/src/Ninject/Settings.StyleCop @@ -1,4 +1,9 @@ + + + castable + +