// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc
{
///
/// Default implementation for .
///
public class DefaultControllerFactory : IControllerFactory
{
private readonly IControllerActivator _controllerActivator;
private readonly IDictionary> _valueAccessorLookup;
private readonly ConcurrentDictionary[]> _activateActions;
private readonly Func[]> _getPropertiesToActivate;
private readonly Func> _getRequiredService = GetRequiredService;
///
/// Initializes a new instance of .
///
/// used to create controller
/// instances.
public DefaultControllerFactory(IControllerActivator controllerActivator)
{
_controllerActivator = controllerActivator;
_valueAccessorLookup = CreateValueAccessorLookup();
_activateActions = new ConcurrentDictionary[]>();
_getPropertiesToActivate = GetPropertiesToActivate;
}
///
/// The used to create a controller.
///
protected IControllerActivator ControllerActivator
{
get
{
return _controllerActivator;
}
}
///
public virtual object CreateController([NotNull] ActionContext actionContext)
{
var actionDescriptor = actionContext.ActionDescriptor as ControllerActionDescriptor;
if (actionDescriptor == null)
{
throw new ArgumentException(
Resources.FormatActionDescriptorMustBeBasedOnControllerAction(
typeof(ControllerActionDescriptor)),
nameof(actionContext));
}
var controllerType = actionDescriptor.ControllerTypeInfo.AsType();
var controllerTypeInfo = controllerType.GetTypeInfo();
if (controllerTypeInfo.IsValueType ||
controllerTypeInfo.IsInterface ||
controllerTypeInfo.IsAbstract ||
(controllerTypeInfo.IsGenericType && controllerTypeInfo.IsGenericTypeDefinition))
{
var message = Resources.FormatValueInterfaceAbstractOrOpenGenericTypesCannotBeActivated(
controllerType.FullName, GetType().FullName);
throw new InvalidOperationException(message);
}
var controller = _controllerActivator.Create(actionContext, controllerType);
ActivateProperties(controller, actionContext);
return controller;
}
///
public virtual void ReleaseController(object controller)
{
var disposableController = controller as IDisposable;
if (disposableController != null)
{
disposableController.Dispose();
}
}
///
/// Activates the specified controller using the specified action context.
///
/// The controller to activate.
/// The context of the executing action.
protected virtual void ActivateProperties([NotNull] object controller, [NotNull] ActionContext context)
{
var controllerType = controller.GetType();
var propertiesToActivate = _activateActions.GetOrAdd(controllerType,
_getPropertiesToActivate);
for (var i = 0; i < propertiesToActivate.Length; i++)
{
var activateInfo = propertiesToActivate[i];
activateInfo.Activate(controller, context);
}
}
///
/// Returns a of property types to delegates used to activate
/// controller properties annotated with .
///
/// A dictionary containing the property type to activator delegate mapping.
/// Override this method to provide custom activation behavior for controller properties
/// annotated with .
protected virtual IDictionary> CreateValueAccessorLookup()
{
var dictionary = new Dictionary>
{
{ typeof(ActionContext), (context) => context },
{ typeof(HttpContext), (context) => context.HttpContext },
{ typeof(HttpRequest), (context) => context.HttpContext.Request },
{ typeof(HttpResponse), (context) => context.HttpContext.Response },
{
typeof(ViewDataDictionary),
(context) =>
{
var serviceProvider = context.HttpContext.RequestServices;
return new ViewDataDictionary(
serviceProvider.GetRequiredService(),
context.ModelState);
}
},
{
typeof(ActionBindingContext),
(context) =>
{
var serviceProvider = context.HttpContext.RequestServices;
var accessor = serviceProvider.GetRequiredService>();
return accessor.Value;
}
}
};
return dictionary;
}
private PropertyActivator[] GetPropertiesToActivate(Type type)
{
var activatorsForActivateProperties = PropertyActivator.GetPropertiesToActivate(
type,
typeof(ActivateAttribute),
CreateActivateInfo);
return activatorsForActivateProperties;
}
private PropertyActivator CreateActivateInfo(
PropertyInfo property)
{
Func valueAccessor;
if (!_valueAccessorLookup.TryGetValue(property.PropertyType, out valueAccessor))
{
var message = Resources.FormatControllerFactory_PropertyCannotBeActivated(
property.Name,
property.DeclaringType.FullName);
throw new InvalidOperationException(message);
}
return new PropertyActivator(property, valueAccessor);
}
private PropertyActivator CreateFromServicesInfo(
PropertyInfo property)
{
var valueAccessor = _getRequiredService(property.PropertyType);
return new PropertyActivator(property, valueAccessor);
}
private static Func GetRequiredService(Type propertyType)
{
return actionContext => actionContext.HttpContext.RequestServices.GetRequiredService(propertyType);
}
}
}