PageRenderTime 66ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/src/System.Web.Mvc/DefaultControllerFactory.cs

https://bitbucket.org/mdavid/aspnetwebstack
C# | 294 lines | 252 code | 32 blank | 10 comment | 38 complexity | 3c0daaeea8c6c92d572fa92f5ffc5b78 MD5 | raw file
  1. using System.Collections.Concurrent;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Web.Mvc.Properties;
  7. using System.Web.Routing;
  8. using System.Web.SessionState;
  9. namespace System.Web.Mvc
  10. {
  11. public class DefaultControllerFactory : IControllerFactory
  12. {
  13. private static readonly ConcurrentDictionary<Type, SessionStateBehavior> _sessionStateCache = new ConcurrentDictionary<Type, SessionStateBehavior>();
  14. private static ControllerTypeCache _staticControllerTypeCache = new ControllerTypeCache();
  15. private IBuildManager _buildManager;
  16. private IResolver<IControllerActivator> _activatorResolver;
  17. private IControllerActivator _controllerActivator;
  18. private ControllerBuilder _controllerBuilder;
  19. private ControllerTypeCache _instanceControllerTypeCache;
  20. public DefaultControllerFactory()
  21. : this(null, null, null)
  22. {
  23. }
  24. public DefaultControllerFactory(IControllerActivator controllerActivator)
  25. : this(controllerActivator, null, null)
  26. {
  27. }
  28. internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolver<IControllerActivator> activatorResolver, IDependencyResolver dependencyResolver)
  29. {
  30. if (controllerActivator != null)
  31. {
  32. _controllerActivator = controllerActivator;
  33. }
  34. else
  35. {
  36. _activatorResolver = activatorResolver ?? new SingleServiceResolver<IControllerActivator>(
  37. () => null,
  38. new DefaultControllerActivator(dependencyResolver),
  39. "DefaultControllerFactory constructor");
  40. }
  41. }
  42. private IControllerActivator ControllerActivator
  43. {
  44. get
  45. {
  46. if (_controllerActivator != null)
  47. {
  48. return _controllerActivator;
  49. }
  50. _controllerActivator = _activatorResolver.Current;
  51. return _controllerActivator;
  52. }
  53. }
  54. internal IBuildManager BuildManager
  55. {
  56. get
  57. {
  58. if (_buildManager == null)
  59. {
  60. _buildManager = new BuildManagerWrapper();
  61. }
  62. return _buildManager;
  63. }
  64. set { _buildManager = value; }
  65. }
  66. internal ControllerBuilder ControllerBuilder
  67. {
  68. get { return _controllerBuilder ?? ControllerBuilder.Current; }
  69. set { _controllerBuilder = value; }
  70. }
  71. internal ControllerTypeCache ControllerTypeCache
  72. {
  73. get { return _instanceControllerTypeCache ?? _staticControllerTypeCache; }
  74. set { _instanceControllerTypeCache = value; }
  75. }
  76. internal static InvalidOperationException CreateAmbiguousControllerException(RouteBase route, string controllerName, ICollection<Type> matchingTypes)
  77. {
  78. // we need to generate an exception containing all the controller types
  79. StringBuilder typeList = new StringBuilder();
  80. foreach (Type matchedType in matchingTypes)
  81. {
  82. typeList.AppendLine();
  83. typeList.Append(matchedType.FullName);
  84. }
  85. string errorText;
  86. Route castRoute = route as Route;
  87. if (castRoute != null)
  88. {
  89. errorText = String.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ControllerNameAmbiguous_WithRouteUrl,
  90. controllerName, castRoute.Url, typeList);
  91. }
  92. else
  93. {
  94. errorText = String.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ControllerNameAmbiguous_WithoutRouteUrl,
  95. controllerName, typeList);
  96. }
  97. return new InvalidOperationException(errorText);
  98. }
  99. public virtual IController CreateController(RequestContext requestContext, string controllerName)
  100. {
  101. if (requestContext == null)
  102. {
  103. throw new ArgumentNullException("requestContext");
  104. }
  105. if (String.IsNullOrEmpty(controllerName))
  106. {
  107. throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
  108. }
  109. Type controllerType = GetControllerType(requestContext, controllerName);
  110. IController controller = GetControllerInstance(requestContext, controllerType);
  111. return controller;
  112. }
  113. protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
  114. {
  115. if (controllerType == null)
  116. {
  117. throw new HttpException(404,
  118. String.Format(
  119. CultureInfo.CurrentCulture,
  120. MvcResources.DefaultControllerFactory_NoControllerFound,
  121. requestContext.HttpContext.Request.Path));
  122. }
  123. if (!typeof(IController).IsAssignableFrom(controllerType))
  124. {
  125. throw new ArgumentException(
  126. String.Format(
  127. CultureInfo.CurrentCulture,
  128. MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase,
  129. controllerType),
  130. "controllerType");
  131. }
  132. return ControllerActivator.Create(requestContext, controllerType);
  133. }
  134. protected internal virtual SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
  135. {
  136. if (controllerType == null)
  137. {
  138. return SessionStateBehavior.Default;
  139. }
  140. return _sessionStateCache.GetOrAdd(
  141. controllerType,
  142. type =>
  143. {
  144. var attr = type.GetCustomAttributes(typeof(SessionStateAttribute), inherit: true)
  145. .OfType<SessionStateAttribute>()
  146. .FirstOrDefault();
  147. return (attr != null) ? attr.Behavior : SessionStateBehavior.Default;
  148. });
  149. }
  150. protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName)
  151. {
  152. if (String.IsNullOrEmpty(controllerName))
  153. {
  154. throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
  155. }
  156. // first search in the current route's namespace collection
  157. object routeNamespacesObj;
  158. Type match;
  159. if (requestContext != null && requestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj))
  160. {
  161. IEnumerable<string> routeNamespaces = routeNamespacesObj as IEnumerable<string>;
  162. if (routeNamespaces != null && routeNamespaces.Any())
  163. {
  164. HashSet<string> namespaceHash = new HashSet<string>(routeNamespaces, StringComparer.OrdinalIgnoreCase);
  165. match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, namespaceHash);
  166. // the UseNamespaceFallback key might not exist, in which case its value is implicitly "true"
  167. if (match != null || false.Equals(requestContext.RouteData.DataTokens["UseNamespaceFallback"]))
  168. {
  169. // got a match or the route requested we stop looking
  170. return match;
  171. }
  172. }
  173. }
  174. // then search in the application's default namespace collection
  175. if (ControllerBuilder.DefaultNamespaces.Count > 0)
  176. {
  177. HashSet<string> namespaceDefaults = new HashSet<string>(ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);
  178. match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, namespaceDefaults);
  179. if (match != null)
  180. {
  181. return match;
  182. }
  183. }
  184. // if all else fails, search every namespace
  185. return GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, null /* namespaces */);
  186. }
  187. private Type GetControllerTypeWithinNamespaces(RouteBase route, string controllerName, HashSet<string> namespaces)
  188. {
  189. // Once the master list of controllers has been created we can quickly index into it
  190. ControllerTypeCache.EnsureInitialized(BuildManager);
  191. ICollection<Type> matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName, namespaces);
  192. switch (matchingTypes.Count)
  193. {
  194. case 0:
  195. // no matching types
  196. return null;
  197. case 1:
  198. // single matching type
  199. return matchingTypes.First();
  200. default:
  201. // multiple matching types
  202. throw CreateAmbiguousControllerException(route, controllerName, matchingTypes);
  203. }
  204. }
  205. public virtual void ReleaseController(IController controller)
  206. {
  207. IDisposable disposable = controller as IDisposable;
  208. if (disposable != null)
  209. {
  210. disposable.Dispose();
  211. }
  212. }
  213. SessionStateBehavior IControllerFactory.GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
  214. {
  215. if (requestContext == null)
  216. {
  217. throw new ArgumentNullException("requestContext");
  218. }
  219. if (String.IsNullOrEmpty(controllerName))
  220. {
  221. throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
  222. }
  223. Type controllerType = GetControllerType(requestContext, controllerName);
  224. return GetControllerSessionBehavior(requestContext, controllerType);
  225. }
  226. private class DefaultControllerActivator : IControllerActivator
  227. {
  228. private Func<IDependencyResolver> _resolverThunk;
  229. public DefaultControllerActivator()
  230. : this(null)
  231. {
  232. }
  233. public DefaultControllerActivator(IDependencyResolver resolver)
  234. {
  235. if (resolver == null)
  236. {
  237. _resolverThunk = () => DependencyResolver.Current;
  238. }
  239. else
  240. {
  241. _resolverThunk = () => resolver;
  242. }
  243. }
  244. public IController Create(RequestContext requestContext, Type controllerType)
  245. {
  246. try
  247. {
  248. return (IController)(_resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
  249. }
  250. catch (Exception ex)
  251. {
  252. throw new InvalidOperationException(
  253. String.Format(
  254. CultureInfo.CurrentCulture,
  255. MvcResources.DefaultControllerFactory_ErrorCreatingController,
  256. controllerType),
  257. ex);
  258. }
  259. }
  260. }
  261. }
  262. }