// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.Contracts; using System.Net.Http; using System.Threading; using System.Web.Http.Controllers; using System.Web.Http.Internal; using System.Web.Http.Properties; namespace System.Web.Http.Dispatcher { /// /// Default implementation of an . /// A different implementation can be registered via the . /// We optimize for the case where we have an /// instance per instance but can support cases where there are /// many instances for one /// as well. In the latter case the lookup is slightly slower because it goes through the /// dictionary. /// public class DefaultHttpControllerActivator : IHttpControllerActivator { private Tuple> _fastCache; private object _cacheKey = new object(); /// /// Creates the specified by using the given /// /// The request message. /// Type of the controller. /// The controller descriptor /// An instance of type . public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { if (request == null) { throw Error.ArgumentNull("request"); } if (controllerDescriptor == null) { throw Error.ArgumentNull("controllerDescriptor"); } if (controllerType == null) { throw Error.ArgumentNull("controllerType"); } try { Func activator; // First check in the local fast cache and if not a match then look in the broader // HttpControllerDescriptor.Properties cache if (_fastCache == null) { IHttpController controller = GetInstanceOrActivator(request, controllerType, out activator); if (controller != null) { // we have a controller registered with the dependency resolver for this controller type return controller; } else { Tuple> cacheItem = Tuple.Create(controllerDescriptor, activator); Interlocked.CompareExchange(ref _fastCache, cacheItem, null); } } else if (_fastCache.Item1 == controllerDescriptor) { // If the key matches and we already have the delegate for creating an instance. activator = _fastCache.Item2; } else { // If the key doesn't match then lookup/create delegate in the HttpControllerDescriptor.Properties for // that HttpControllerDescriptor instance object value; if (controllerDescriptor.Properties.TryGetValue(_cacheKey, out value)) { activator = (Func)value; } else { IHttpController controller = GetInstanceOrActivator(request, controllerType, out activator); if (controller != null) { // we have a controller registered with the dependency resolver for this controller type return controller; } else { controllerDescriptor.Properties.TryAdd(_cacheKey, activator); } } } return activator(); } catch (Exception ex) { throw Error.InvalidOperation(ex, SRResources.DefaultControllerFactory_ErrorCreatingController, controllerType.Name); } } // Returns the controller instance from the dependency resolver if there is one registered // else returns the activator that calls the default ctor for the give controllerType. private static IHttpController GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, out Func activator) { Contract.Assert(request != null); Contract.Assert(controllerType != null); // If dependency resolver returns controller object then use it. IHttpController instance = (IHttpController)request.GetDependencyScope().GetService(controllerType); if (instance != null) { activator = null; return instance; } // Otherwise create a delegate for creating a new instance of the type activator = TypeActivator.Create(controllerType); return null; } } }