1 /* ****************************************************************************
\r
3 * Copyright (c) Microsoft Corporation. All rights reserved.
\r
5 * This software is subject to the Microsoft Public License (Ms-PL).
\r
6 * A copy of the license can be found in the license.htm file included
\r
7 * in this distribution.
\r
9 * You must not remove this notice, or any other, from this software.
\r
11 * ***************************************************************************/
\r
13 namespace System.Web.Mvc {
\r
15 using System.Collections;
\r
16 using System.Collections.Generic;
\r
17 using System.Globalization;
\r
21 using System.Web.Mvc.Resources;
\r
22 using System.Web.Routing;
\r
24 public class DefaultControllerFactory : IControllerFactory {
\r
26 private IBuildManager _buildManager;
\r
27 private ControllerBuilder _controllerBuilder;
\r
28 private ControllerTypeCache _instanceControllerTypeCache;
\r
29 private static ControllerTypeCache _staticControllerTypeCache = new ControllerTypeCache();
\r
31 internal IBuildManager BuildManager {
\r
33 if (_buildManager == null) {
\r
34 _buildManager = new BuildManagerWrapper();
\r
36 return _buildManager;
\r
39 _buildManager = value;
\r
43 internal ControllerBuilder ControllerBuilder {
\r
45 return _controllerBuilder ?? ControllerBuilder.Current;
\r
48 _controllerBuilder = value;
\r
52 internal ControllerTypeCache ControllerTypeCache {
\r
54 return _instanceControllerTypeCache ?? _staticControllerTypeCache;
\r
57 _instanceControllerTypeCache = value;
\r
61 internal static InvalidOperationException CreateAmbiguousControllerException(RouteBase route, string controllerName, ICollection<Type> matchingTypes) {
\r
62 // we need to generate an exception containing all the controller types
\r
63 StringBuilder typeList = new StringBuilder();
\r
64 foreach (Type matchedType in matchingTypes) {
\r
65 typeList.AppendLine();
\r
66 typeList.Append(matchedType.FullName);
\r
70 Route castRoute = route as Route;
\r
71 if (castRoute != null) {
\r
72 errorText = String.Format(CultureInfo.CurrentUICulture, MvcResources.DefaultControllerFactory_ControllerNameAmbiguous_WithRouteUrl,
\r
73 controllerName, castRoute.Url, typeList);
\r
76 errorText = String.Format(CultureInfo.CurrentUICulture, MvcResources.DefaultControllerFactory_ControllerNameAmbiguous_WithoutRouteUrl,
\r
77 controllerName, typeList);
\r
80 return new InvalidOperationException(errorText);
\r
83 public virtual IController CreateController(RequestContext requestContext, string controllerName) {
\r
84 if (requestContext == null) {
\r
85 throw new ArgumentNullException("requestContext");
\r
87 if (String.IsNullOrEmpty(controllerName)) {
\r
88 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
\r
90 Type controllerType = GetControllerType(requestContext, controllerName);
\r
91 IController controller = GetControllerInstance(requestContext, controllerType);
\r
95 protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType) {
\r
96 if (controllerType == null) {
\r
97 throw new HttpException(404,
\r
99 CultureInfo.CurrentUICulture,
\r
100 MvcResources.DefaultControllerFactory_NoControllerFound,
\r
101 requestContext.HttpContext.Request.Path));
\r
103 if (!typeof(IController).IsAssignableFrom(controllerType)) {
\r
104 throw new ArgumentException(
\r
106 CultureInfo.CurrentUICulture,
\r
107 MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase,
\r
112 return (IController)Activator.CreateInstance(controllerType);
\r
114 catch (Exception ex) {
\r
115 throw new InvalidOperationException(
\r
117 CultureInfo.CurrentUICulture,
\r
118 MvcResources.DefaultControllerFactory_ErrorCreatingController,
\r
124 protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName) {
\r
125 if (String.IsNullOrEmpty(controllerName)) {
\r
126 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
\r
129 // first search in the current route's namespace collection
\r
130 object routeNamespacesObj;
\r
132 if (requestContext != null && requestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj)) {
\r
133 IEnumerable<string> routeNamespaces = routeNamespacesObj as IEnumerable<string>;
\r
134 if (routeNamespaces != null && routeNamespaces.Any()) {
\r
135 HashSet<string> nsHash = new HashSet<string>(routeNamespaces, StringComparer.OrdinalIgnoreCase);
\r
136 match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, nsHash);
\r
138 // the UseNamespaceFallback key might not exist, in which case its value is implicitly "true"
\r
139 if (match != null || false.Equals(requestContext.RouteData.DataTokens["UseNamespaceFallback"])) {
\r
140 // got a match or the route requested we stop looking
\r
146 // then search in the application's default namespace collection
\r
147 if (ControllerBuilder.DefaultNamespaces.Count > 0) {
\r
148 HashSet<string> nsDefaults = new HashSet<string>(ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);
\r
149 match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, nsDefaults);
\r
150 if (match != null) {
\r
155 // if all else fails, search every namespace
\r
156 return GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, null /* namespaces */);
\r
159 private Type GetControllerTypeWithinNamespaces(RouteBase route, string controllerName, HashSet<string> namespaces) {
\r
160 // Once the master list of controllers has been created we can quickly index into it
\r
161 ControllerTypeCache.EnsureInitialized(BuildManager);
\r
163 ICollection<Type> matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName, namespaces);
\r
164 switch (matchingTypes.Count) {
\r
166 // no matching types
\r
170 // single matching type
\r
171 return matchingTypes.First();
\r
174 // multiple matching types
\r
175 throw CreateAmbiguousControllerException(route, controllerName, matchingTypes);
\r
179 public virtual void ReleaseController(IController controller) {
\r
180 IDisposable disposable = controller as IDisposable;
\r
181 if (disposable != null) {
\r
182 disposable.Dispose();
\r