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