Skip to content

Commit

Permalink
Merge branch 'feature-684' of https://github.com/simpleinjector/Simpl…
Browse files Browse the repository at this point in the history
…eInjector into v4.6.x
  • Loading branch information
dotnetjunkie committed May 10, 2019
2 parents 0ca698d + b0dd1eb commit dcc9cf6
Show file tree
Hide file tree
Showing 10 changed files with 713 additions and 7 deletions.
@@ -0,0 +1,208 @@
#region Copyright Simple Injector Contributors
/* The Simple Injector is an easy-to-use Inversion of Control library for .NET
*
* Copyright (c) 2019 Simple Injector Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
* following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
* EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#endregion

namespace SimpleInjector
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.ViewComponents;
using Microsoft.Extensions.DependencyInjection;
using SimpleInjector.Diagnostics;
using SimpleInjector.Integration.AspNetCore;
using SimpleInjector.Integration.AspNetCore.Mvc;

/// <summary>
/// Extension methods for <see cref="SimpleInjectorAspNetCoreBuilder"/> that allow integrating
/// Simple Injector with ASP.NET Core MVC controllers and view components.
/// </summary>
public static class SimpleInjectorAspNetCoreBuilderMvcCoreExtensions
{
/// <summary>
/// Registers all application's controllers in Simple Injector and instructs ASP.NET Core to let
/// Simple Injector create those controllers.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <returns>The supplied <paramref name="builder"/> instance.</returns>
public static SimpleInjectorAspNetCoreBuilder AddControllerActivation(
this SimpleInjectorAspNetCoreBuilder builder)
{
Requires.IsNotNull(builder, nameof(builder));

ApplicationPartManager manager = GetApplicationPartManager(
builder.Services,
nameof(AddControllerActivation));

RegisterMvcControllers(builder.Container, manager);

builder.Services.AddSingleton<IControllerActivator>(
new SimpleInjectorControllerActivator(builder.Container));

return builder;
}

/// <summary>
/// Registers all application's view components in Simple Injector and instructs ASP.NET Core to let
/// Simple Injector create those view components.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <returns>The supplied <paramref name="builder"/> instance.</returns>
public static SimpleInjectorAspNetCoreBuilder AddViewComponentActivation(
this SimpleInjectorAspNetCoreBuilder builder)
{
Requires.IsNotNull(builder, nameof(builder));

RegisterViewComponentTypes(builder);

builder.Services.AddSingleton<IViewComponentActivator>(
new SimpleInjectorViewComponentActivator(builder.Container));

return builder;
}

private static ApplicationPartManager GetApplicationPartManager(
this IServiceCollection services, string methodName)
{
ServiceDescriptor descriptor = services
.LastOrDefault(d => d.ServiceType == typeof(ApplicationPartManager));

if (descriptor is null)
{
throw new InvalidOperationException(
string.Format(
CultureInfo.InvariantCulture,
"A registration for the {0} is missing from the ASP.NET Core configuration " +
"system. This is most likely caused by a missing call to services.AddMvcCore() or " +
"services.AddMvc() as part of the ConfigureServices(IServiceCollection) method of " +
"the Startup class. A call to one of those methods will ensure the registration " +
"of the {1}.",
typeof(ApplicationPartManager).FullName,
typeof(ApplicationPartManager).Name));
}
else if (descriptor.ImplementationInstance is null)
{
throw new InvalidOperationException(
string.Format(
CultureInfo.InvariantCulture,
"Although a registration for {0} exists in the ASP.NET Core configuration system, " +
"the registration is not added as an existing instance. This makes it impossible " +
"for Simple Injector's {1} method to get this instance from ASP.NET Core's " +
"IServiceCollection. This is most likely because {2} was overridden by you or a " +
"third-party library. Make sure that you use the AddSingleton overload that takes " +
"in an existing instance—i.e. call " +
"services.AddSingleton<{2}>(new {2}()).",
typeof(ApplicationPartManager).FullName,
methodName,
typeof(ApplicationPartManager).Name));
}
else
{
return (ApplicationPartManager)descriptor.ImplementationInstance;
}
}

private static void RegisterMvcControllers(Container container, ApplicationPartManager manager)
{
var feature = new ControllerFeature();
manager.PopulateFeature(feature);
var controllerTypes = feature.Controllers.Select(t => t.AsType());

RegisterControllerTypes(container, controllerTypes);
}

private static void RegisterControllerTypes(this Container container, IEnumerable<Type> types)
{
foreach (Type type in types.ToArray())
{
var registration = CreateConcreteRegistration(container, type);

// Microsoft.AspNetCore.Mvc.Controller implements IDisposable (which is a design flaw).
// This will cause false positives in Simple Injector's diagnostic services, so we suppress
// this warning in case the registered type doesn't override Dispose from Controller.
if (ShouldSuppressDisposingControllers(type))
{
registration.SuppressDiagnosticWarning(
DiagnosticType.DisposableTransientComponent,
"Derived type doesn't override Dispose, so it can be safely ignored.");
}

container.AddRegistration(type, registration);
}
}

// The user should be warned when he implements IDisposable on a non-controller derivative,
// and otherwise only if he has overridden Controller.Dispose(bool).
private static bool ShouldSuppressDisposingControllers(Type controllerType) =>
TypeInheritsFromController(controllerType)
&& GetProtectedDisposeMethod(controllerType).DeclaringType == typeof(Controller);

private static bool TypeInheritsFromController(Type controllerType) =>
typeof(Controller).GetTypeInfo().IsAssignableFrom(controllerType);

private static MethodInfo GetProtectedDisposeMethod(Type controllerType)
{
foreach (var method in controllerType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance))
{
// if method == 'protected void Dispose(bool)'
if (!method.IsPrivate
&& !method.IsPublic
&& method.ReturnType == typeof(void)
&& method.Name == "Dispose"
&& method.GetParameters().Length == 1
&& method.GetParameters()[0].ParameterType == typeof(bool))
{
return method;
}
}

return null;
}

private static void RegisterViewComponentTypes(SimpleInjectorAspNetCoreBuilder builder)
{
var container = builder.Container;

ApplicationPartManager manager = GetApplicationPartManager(
builder.Services,
nameof(AddViewComponentActivation));

var feature = new ViewComponentFeature();
manager.PopulateFeature(feature);
var viewComponentTypes = feature.ViewComponents.Select(info => info.AsType());

foreach (Type type in viewComponentTypes.ToArray())
{
container.AddRegistration(type, CreateConcreteRegistration(container, type));
}
}

private static Registration CreateConcreteRegistration(Container container, Type concreteType) =>
container.Options.LifestyleSelectionBehavior
.SelectLifestyle(concreteType)
.CreateRegistration(concreteType, container);
}
}
Expand Up @@ -47,8 +47,16 @@ public static class SimpleInjectorAspNetCoreMvcIntegrationExtensions
/// <param name="container">The container the controllers should be registered in.</param>
/// <param name="applicationBuilder">The ASP.NET object that holds the application's configuration.
/// </param>
public static void RegisterMvcControllers(this Container container,
IApplicationBuilder applicationBuilder)
[Obsolete(
"Please use " + nameof(SimpleInjectorAspNetCoreBuilderMvcCoreExtensions) + "." +
nameof(SimpleInjectorAspNetCoreBuilderMvcCoreExtensions.AddControllerActivation) + " instead" +
"—e.g. 'services.AddSimpleInjector(container, options => options." +
nameof(SimpleInjectorAspNetCoreBuilderMvcCoreExtensions.AddControllerActivation) + "());'. " +
"Please see https://simpleinjector.org/aspnetcore for more details. " +
"Will be treated as an error from version 5.0. Will be removed in version 6.0.",
error: false)]
public static void RegisterMvcControllers(
this Container container, IApplicationBuilder applicationBuilder)
{
Requires.IsNotNull(container, nameof(container));
Requires.IsNotNull(applicationBuilder, nameof(applicationBuilder));
Expand Down Expand Up @@ -82,8 +90,16 @@ public static class SimpleInjectorAspNetCoreMvcIntegrationExtensions
/// <param name="applicationBuilder">The ASP.NET object that holds the
/// <see cref="IViewComponentDescriptorProvider"/> that allows retrieving the application's controller types.
/// </param>
public static void RegisterMvcViewComponents(this Container container,
IApplicationBuilder applicationBuilder)
[Obsolete(
"Please use " + nameof(SimpleInjectorAspNetCoreBuilderMvcCoreExtensions) + "." +
nameof(SimpleInjectorAspNetCoreBuilderMvcCoreExtensions.AddViewComponentActivation) + " instead" +
"—e.g. 'services.AddSimpleInjector(container, options => options." +
nameof(SimpleInjectorAspNetCoreBuilderMvcCoreExtensions.AddViewComponentActivation) + "());'. " +
"Please see https://simpleinjector.org/aspnetcore for more details. " +
"Will be treated as an error from version 5.0. Will be removed in version 6.0.",
error: false)]
public static void RegisterMvcViewComponents(
this Container container, IApplicationBuilder applicationBuilder)
{
Requires.IsNotNull(container, nameof(container));
Requires.IsNotNull(applicationBuilder, nameof(applicationBuilder));
Expand Down Expand Up @@ -116,8 +132,16 @@ public static class SimpleInjectorAspNetCoreMvcIntegrationExtensions
/// <param name="container">The container the controllers should be registered in.</param>
/// <param name="viewComponentDescriptorProvider">The provider that contains the list of view
/// components to register.</param>
public static void RegisterMvcViewComponents(this Container container,
IViewComponentDescriptorProvider viewComponentDescriptorProvider)
[Obsolete(
"Please use " + nameof(SimpleInjectorAspNetCoreBuilderMvcCoreExtensions) + "." +
nameof(SimpleInjectorAspNetCoreBuilderMvcCoreExtensions.AddViewComponentActivation) + " instead" +
"—e.g. 'services.AddSimpleInjector(container, options => options." +
nameof(SimpleInjectorAspNetCoreBuilderMvcCoreExtensions.AddViewComponentActivation) + "());'. " +
"Please see https://simpleinjector.org/aspnetcore for more details. " +
"Will be treated as an error from version 5.0. Will be removed in version 6.0.",
error: false)]
public static void RegisterMvcViewComponents(
this Container container, IViewComponentDescriptorProvider viewComponentDescriptorProvider)
{
Requires.IsNotNull(container, nameof(container));
Requires.IsNotNull(viewComponentDescriptorProvider, nameof(viewComponentDescriptorProvider));
Expand Down

0 comments on commit dcc9cf6

Please sign in to comment.