diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs index 2c801e963b..46002a6c8d 100644 --- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs @@ -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; } + + /// + /// Adds explicit registrations for controllers with ambiguous constructors to prevent downstream issues for + /// users who wish to use + /// + public static IUmbracoBuilder AddControllersWithAmbiguousConstructors( + this IUmbracoBuilder builder) + { + builder.Services.TryAddTransient(sp => + { + IUserDataService userDataService = sp.GetRequiredService(); + return ActivatorUtilities.CreateInstance(sp, userDataService); + }); + + return builder; + } } } diff --git a/src/Umbraco.Web.UI/Composers/ControllersAsServicesComposer.cs b/src/Umbraco.Web.UI/Composers/ControllersAsServicesComposer.cs new file mode 100644 index 0000000000..042343df67 --- /dev/null +++ b/src/Umbraco.Web.UI/Composers/ControllersAsServicesComposer.cs @@ -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 +{ + /// + /// Adds controllers to the service collection. + /// + /// + /// + /// Umbraco 9 out of the box, makes use of 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. + /// + /// + /// Some users may wish to switch to (perhaps to make use of interception/decoration). + /// + /// + /// This composer exists to help us detect ambiguous constructors in the CMS such that we do not cause unnecessary effort downstream. + /// + /// + /// This Composer is not shipped by the Umbraco.Templates package. + /// + /// + public class ControllersAsServicesComposer : IComposer + { + /// + public void Compose(IUmbracoBuilder builder) => builder.Services + .AddMvc() + .AddControllersAsServicesWithoutChangingActivator(); + } + + internal static class MvcBuilderExtensions + { + /// + /// but without the replacement of + /// . + /// + /// + /// We don't need to opt in to to ensure container validation + /// passes. + /// + 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; + } + } +}