namespace System.Web.Mvc { using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Web; using System.Web.Mvc.Resources; using System.Web.Routing; using System.Web.SessionState; public class DefaultControllerFactory : IControllerFactory { private IBuildManager _buildManager; private IResolver _activatorResolver; private IControllerActivator _controllerActivator; private ControllerBuilder _controllerBuilder; private ControllerTypeCache _instanceControllerTypeCache; private static readonly ConcurrentDictionary _sessionStateCache = new ConcurrentDictionary(); private static ControllerTypeCache _staticControllerTypeCache = new ControllerTypeCache(); public DefaultControllerFactory() : this(null, null, null) { } public DefaultControllerFactory(IControllerActivator controllerActivator) : this(controllerActivator, null, null) { } internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolver activatorResolver, IDependencyResolver dependencyResolver) { if (controllerActivator != null) { _controllerActivator = controllerActivator; } else { _activatorResolver = activatorResolver ?? new SingleServiceResolver( () => null, new DefaultControllerActivator(dependencyResolver), "DefaultControllerFactory contstructor" ); } } private IControllerActivator ControllerActivator { get { if (_controllerActivator != null) { return _controllerActivator; } _controllerActivator = _activatorResolver.Current; return _controllerActivator; } } internal IBuildManager BuildManager { get { if (_buildManager == null) { _buildManager = new BuildManagerWrapper(); } return _buildManager; } set { _buildManager = value; } } internal ControllerBuilder ControllerBuilder { get { return _controllerBuilder ?? ControllerBuilder.Current; } set { _controllerBuilder = value; } } internal ControllerTypeCache ControllerTypeCache { get { return _instanceControllerTypeCache ?? _staticControllerTypeCache; } set { _instanceControllerTypeCache = value; } } internal static InvalidOperationException CreateAmbiguousControllerException(RouteBase route, string controllerName, ICollection matchingTypes) { // we need to generate an exception containing all the controller types StringBuilder typeList = new StringBuilder(); foreach (Type matchedType in matchingTypes) { typeList.AppendLine(); typeList.Append(matchedType.FullName); } string errorText; Route castRoute = route as Route; if (castRoute != null) { errorText = String.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ControllerNameAmbiguous_WithRouteUrl, controllerName, castRoute.Url, typeList); } else { errorText = String.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ControllerNameAmbiguous_WithoutRouteUrl, controllerName, typeList); } return new InvalidOperationException(errorText); } public virtual IController CreateController(RequestContext requestContext, string controllerName) { if (requestContext == null) { throw new ArgumentNullException("requestContext"); } if (String.IsNullOrEmpty(controllerName)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName"); } Type controllerType = GetControllerType(requestContext, controllerName); IController controller = GetControllerInstance(requestContext, controllerType); return controller; } protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType) { if (controllerType == null) { throw new HttpException(404, String.Format( CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_NoControllerFound, requestContext.HttpContext.Request.Path)); } if (!typeof(IController).IsAssignableFrom(controllerType)) { throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase, controllerType), "controllerType"); } return ControllerActivator.Create(requestContext, controllerType); } protected internal virtual SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType) { if (controllerType == null) { return SessionStateBehavior.Default; } return _sessionStateCache.GetOrAdd( controllerType, type => { var attr = type.GetCustomAttributes(typeof(SessionStateAttribute), inherit: true) .OfType() .FirstOrDefault(); return (attr != null) ? attr.Behavior : SessionStateBehavior.Default; } ); } protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName) { if (String.IsNullOrEmpty(controllerName)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName"); } // first search in the current route's namespace collection object routeNamespacesObj; Type match; if (requestContext != null && requestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj)) { IEnumerable routeNamespaces = routeNamespacesObj as IEnumerable; if (routeNamespaces != null && routeNamespaces.Any()) { HashSet nsHash = new HashSet(routeNamespaces, StringComparer.OrdinalIgnoreCase); match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, nsHash); // the UseNamespaceFallback key might not exist, in which case its value is implicitly "true" if (match != null || false.Equals(requestContext.RouteData.DataTokens["UseNamespaceFallback"])) { // got a match or the route requested we stop looking return match; } } } // then search in the application's default namespace collection if (ControllerBuilder.DefaultNamespaces.Count > 0) { HashSet nsDefaults = new HashSet(ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase); match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, nsDefaults); if (match != null) { return match; } } // if all else fails, search every namespace return GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, null /* namespaces */); } private Type GetControllerTypeWithinNamespaces(RouteBase route, string controllerName, HashSet namespaces) { // Once the master list of controllers has been created we can quickly index into it ControllerTypeCache.EnsureInitialized(BuildManager); ICollection matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName, namespaces); switch (matchingTypes.Count) { case 0: // no matching types return null; case 1: // single matching type return matchingTypes.First(); default: // multiple matching types throw CreateAmbiguousControllerException(route, controllerName, matchingTypes); } } public virtual void ReleaseController(IController controller) { IDisposable disposable = controller as IDisposable; if (disposable != null) { disposable.Dispose(); } } SessionStateBehavior IControllerFactory.GetControllerSessionBehavior(RequestContext requestContext, string controllerName) { if (requestContext == null) { throw new ArgumentNullException("requestContext"); } if (String.IsNullOrEmpty(controllerName)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName"); } Type controllerType = GetControllerType(requestContext, controllerName); return GetControllerSessionBehavior(requestContext, controllerType); } private class DefaultControllerActivator : IControllerActivator { Func _resolverThunk; public DefaultControllerActivator() : this(null) { } public DefaultControllerActivator(IDependencyResolver resolver) { if (resolver == null) { _resolverThunk = () => DependencyResolver.Current; } else { _resolverThunk = () => resolver; } } public IController Create(RequestContext requestContext, Type controllerType) { try { return (IController)(_resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType)); } catch (Exception ex) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ErrorCreatingController, controllerType), ex); } } } } }