// 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;
}
}
}