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;
+ }
+ }
+}