using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Web.Mvc; using System.Web.Routing; using Umbraco.Core; namespace Umbraco.Web.Mvc { /// /// A controller factory which uses an internal list of in order to invoke /// different controller factories dependent upon their implementation of for the current /// request. Allows circumvention of MVC3's singly registered IControllerFactory. /// /// internal class MasterControllerFactory : DefaultControllerFactory { private readonly FilteredControllerFactoriesResolver _slaveFactories; private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(); private readonly ConcurrentDictionary _controllerCache = new ConcurrentDictionary(); public MasterControllerFactory(FilteredControllerFactoriesResolver factoryResolver) { _slaveFactories = factoryResolver; } /// /// Creates the specified controller by using the specified request context. /// /// The context of the HTTP request, which includes the HTTP context and route data. /// The name of the controller. /// The controller. /// The parameter is null. /// /// The parameter is null or empty. /// public override IController CreateController(RequestContext requestContext, string controllerName) { var factory = _slaveFactories.Factories.FirstOrDefault(x => x.CanHandle(requestContext)); return factory != null ? factory.CreateController(requestContext, controllerName) : base.CreateController(requestContext, controllerName); } /// /// Retrieves the controller type for the specified name and request context. /// /// /// /// The controller type. /// /// The context of the HTTP request, which includes the HTTP context and route data. /// The name of the controller. internal Type GetControllerTypeInternal(RequestContext requestContext, string controllerName) { var factory = _slaveFactories.Factories.FirstOrDefault(x => x.CanHandle(requestContext)); if (factory != null) { //check to see if the factory is of type UmbracoControllerFactory which exposes the GetControllerType method so we don't have to create // an instance of the controller to figure out what it is. This is a work around for not having a breaking change for: // http://issues.umbraco.org/issue/U4-1726 var umbFactory = factory as UmbracoControllerFactory; if (umbFactory != null) { return umbFactory.GetControllerType(requestContext, controllerName); } //we have no choice but to instantiate the controller var instance = factory.CreateController(requestContext, controllerName); if (instance != null) { return instance.GetType(); } return null; } return base.GetControllerType(requestContext, controllerName); } /// /// Releases the specified controller. /// /// The controller to release. /// public override void ReleaseController(IController controller) { using (new WriteLock(_locker)) { bool released = false; if (controller is Controller) { var requestContext = ((Controller)controller).ControllerContext.RequestContext; var factory = _slaveFactories.Factories.FirstOrDefault(x => x.CanHandle(requestContext)); if (factory != null) { factory.ReleaseController(controller); released = true; } } if (!released) base.ReleaseController(controller); } } } }