Prevent issues for those who wish use ServiceBasedControllerActivator.

Adds registration for CurrentUserController which has ambiguous constructors.
Register our controllers so that issues are visible during dev/test.
This commit is contained in:
Paul Johnson
2022-01-17 10:38:34 +00:00
parent 576f90ad37
commit 366a8ba565
2 changed files with 80 additions and 1 deletions

View File

@@ -1,6 +1,7 @@
using System;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.DependencyInjection;
@@ -54,7 +55,8 @@ namespace Umbraco.Extensions
.AddCoreNotifications()
.AddLogViewer()
.AddExamine()
.AddExamineIndexes();
.AddExamineIndexes()
.AddControllersWithAmbiguousConstructors();
public static IUmbracoBuilder AddUnattendedInstallInstallCreateUser(this IUmbracoBuilder builder)
{
@@ -119,5 +121,21 @@ namespace Umbraco.Extensions
return builder;
}
/// <summary>
/// Adds explicit registrations for controllers with ambiguous constructors to prevent downstream issues for
/// users who wish to use <see cref="Microsoft.AspNetCore.Mvc.Controllers.ServiceBasedControllerActivator"/>
/// </summary>
public static IUmbracoBuilder AddControllersWithAmbiguousConstructors(
this IUmbracoBuilder builder)
{
builder.Services.TryAddTransient(sp =>
{
IUserDataService userDataService = sp.GetRequiredService<IUserDataService>();
return ActivatorUtilities.CreateInstance<CurrentUserController>(sp, userDataService);
});
return builder;
}
}
}

View File

@@ -0,0 +1,61 @@
using System;
using System.Linq;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.DependencyInjection;
namespace Umbraco.Cms.Web.UI.Composers
{
/// <summary>
/// Adds controllers to the service collection.
/// </summary>
/// <remarks>
/// <para>
/// Umbraco 9 out of the box, makes use of <see cref="DefaultControllerActivator"/> which doesn't resolve controller
/// instances from the IOC container, instead it resolves the required dependencies of the controller and constructs an instance
/// of the controller.
/// </para>
/// <para>
/// Some users may wish to switch to <see cref="ServiceBasedControllerActivator"/> (perhaps to make use of interception/decoration).
/// </para>
/// <para>
/// This composer exists to help us detect ambiguous constructors in the CMS such that we do not cause unnecessary effort downstream.
/// </para>
/// <para>
/// This Composer is not shipped by the Umbraco.Templates package.
/// </para>
/// </remarks>
public class ControllersAsServicesComposer : IComposer
{
/// <inheritdoc />
public void Compose(IUmbracoBuilder builder) => builder.Services
.AddMvc()
.AddControllersAsServicesWithoutChangingActivator();
}
internal static class MvcBuilderExtensions
{
/// <summary>
/// <see cref="MvcCoreMvcBuilderExtensions.AddControllersAsServices"/> but without the replacement of
/// <see cref="DefaultControllerActivator"/>.
/// </summary>
/// <remarks>
/// We don't need to opt in to <see cref="ServiceBasedControllerActivator"/> to ensure container validation
/// passes.
/// </remarks>
public static IMvcBuilder AddControllersAsServicesWithoutChangingActivator(this IMvcBuilder builder)
{
var feature = new ControllerFeature();
builder.PartManager.PopulateFeature(feature);
foreach (Type controller in feature.Controllers.Select(c => c.AsType()))
{
builder.Services.TryAddTransient(controller, controller);
}
return builder;
}
}
}