ControllerActivator should be able to use controllers registered as

services

* Added WithControllersFromServiceProvider that replaces the default
  controller activator with a service based one.
* Move activation to DefaultControllerFactory
* Modify [Activate] behavior so that it no longer activates services. Use
  [FromService] attribute to hydrate services

Fixes #1707
This commit is contained in:
Pranav K 2015-01-27 12:25:20 -08:00
parent 7cb6c1065c
commit e1e43e1e8c
59 changed files with 1814 additions and 829 deletions

42
Mvc.sln
View File

@ -132,6 +132,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Xml.Te
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "FormatFilterWebSite", "test\WebSites\FormatFilterWebSite\FormatFilterWebSite.kproj", "{AC9BE567-540E-4C70-90C2-AAF021307A80}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ControllersFromServicesWebSite", "test\WebSites\ControllersFromServicesWebSite\ControllersFromServicesWebSite.kproj", "{983741B2-4424-4ED1-9B03-7675A67230C8}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ControllersFromServicesClassLibrary", "test\WebSites\ControllersFromServicesClassLibrary\ControllersFromServicesClassLibrary.kproj", "{551DC89E-2A13-4CF2-83D7-1ADD802443D5}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "RazorCompilerCacheWebSite", "test\WebSites\RazorCompilerCacheWebSite\RazorCompilerCacheWebSite.kproj", "{42C5D417-4060-48F4-BB28-E9E179007779}"
EndProject
Global
@ -732,6 +736,18 @@ Global
{87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Release|x86.ActiveCfg = Release|Any CPU
{87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Release|x86.Build.0 = Release|Any CPU
{87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Debug|x86.ActiveCfg = Debug|Any CPU
{87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Debug|x86.Build.0 = Debug|Any CPU
{87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Release|Any CPU.Build.0 = Release|Any CPU
{87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Release|x86.ActiveCfg = Release|Any CPU
{87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Release|x86.Build.0 = Release|Any CPU
{22019146-BDFA-442E-8C8E-345FB9644578}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{22019146-BDFA-442E-8C8E-345FB9644578}.Debug|Any CPU.Build.0 = Debug|Any CPU
{22019146-BDFA-442E-8C8E-345FB9644578}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@ -756,6 +772,30 @@ Global
{AC9BE567-540E-4C70-90C2-AAF021307A80}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{AC9BE567-540E-4C70-90C2-AAF021307A80}.Release|x86.ActiveCfg = Release|Any CPU
{AC9BE567-540E-4C70-90C2-AAF021307A80}.Release|x86.Build.0 = Release|Any CPU
{983741B2-4424-4ED1-9B03-7675A67230C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{983741B2-4424-4ED1-9B03-7675A67230C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{983741B2-4424-4ED1-9B03-7675A67230C8}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{983741B2-4424-4ED1-9B03-7675A67230C8}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{983741B2-4424-4ED1-9B03-7675A67230C8}.Debug|x86.ActiveCfg = Debug|Any CPU
{983741B2-4424-4ED1-9B03-7675A67230C8}.Debug|x86.Build.0 = Debug|Any CPU
{983741B2-4424-4ED1-9B03-7675A67230C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{983741B2-4424-4ED1-9B03-7675A67230C8}.Release|Any CPU.Build.0 = Release|Any CPU
{983741B2-4424-4ED1-9B03-7675A67230C8}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{983741B2-4424-4ED1-9B03-7675A67230C8}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{983741B2-4424-4ED1-9B03-7675A67230C8}.Release|x86.ActiveCfg = Release|Any CPU
{983741B2-4424-4ED1-9B03-7675A67230C8}.Release|x86.Build.0 = Release|Any CPU
{551DC89E-2A13-4CF2-83D7-1ADD802443D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{551DC89E-2A13-4CF2-83D7-1ADD802443D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{551DC89E-2A13-4CF2-83D7-1ADD802443D5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{551DC89E-2A13-4CF2-83D7-1ADD802443D5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{551DC89E-2A13-4CF2-83D7-1ADD802443D5}.Debug|x86.ActiveCfg = Debug|Any CPU
{551DC89E-2A13-4CF2-83D7-1ADD802443D5}.Debug|x86.Build.0 = Debug|Any CPU
{551DC89E-2A13-4CF2-83D7-1ADD802443D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{551DC89E-2A13-4CF2-83D7-1ADD802443D5}.Release|Any CPU.Build.0 = Release|Any CPU
{551DC89E-2A13-4CF2-83D7-1ADD802443D5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{551DC89E-2A13-4CF2-83D7-1ADD802443D5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{551DC89E-2A13-4CF2-83D7-1ADD802443D5}.Release|x86.ActiveCfg = Release|Any CPU
{551DC89E-2A13-4CF2-83D7-1ADD802443D5}.Release|x86.Build.0 = Release|Any CPU
{42C5D417-4060-48F4-BB28-E9E179007779}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42C5D417-4060-48F4-BB28-E9E179007779}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42C5D417-4060-48F4-BB28-E9E179007779}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@ -832,6 +872,8 @@ Global
{87AB84B2-22C1-43C6-BB8A-1D327B446FB0} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{22019146-BDFA-442E-8C8E-345FB9644578} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{AC9BE567-540E-4C70-90C2-AAF021307A80} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{983741B2-4424-4ED1-9B03-7675A67230C8} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{551DC89E-2A13-4CF2-83D7-1ADD802443D5} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{42C5D417-4060-48F4-BB28-E9E179007779} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
EndGlobalSection
EndGlobal

View File

@ -92,7 +92,7 @@ namespace MvcSample.Web
return View("MyView", user);
}
[Activate]
[FromServices]
public IHostingEnvironment HostingEnvironment { get; set; }
/// <summary>

View File

@ -7,7 +7,6 @@ using System.Linq;
using System.Reflection;
using Microsoft.AspNet.Mvc.Description;
using Microsoft.AspNet.Mvc.Filters;
using Microsoft.AspNet.Mvc.Logging;
using Microsoft.AspNet.Mvc.Routing;
using Microsoft.Framework.Logging;
@ -34,11 +33,6 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
/// <inheritdoc />
public ControllerModel BuildControllerModel([NotNull] TypeInfo typeInfo)
{
if (!IsController(typeInfo))
{
return null;
}
var controllerModel = CreateControllerModel(typeInfo);
foreach (var methodInfo in typeInfo.AsType().GetMethods())
@ -57,54 +51,6 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
return controllerModel;
}
/// <summary>
/// Returns <c>true</c> if the <paramref name="typeInfo"/> is a controller. Otherwise <c>false</c>.
/// </summary>
/// <param name="typeInfo">The <see cref="TypeInfo"/>.</param>
/// <returns><c>true</c> if the <paramref name="typeInfo"/> is a controller. Otherwise <c>false</c>.</returns>
/// <remarks>
/// Override this method to provide custom logic to determine which types are considered controllers.
/// </remarks>
protected virtual bool IsController([NotNull] TypeInfo typeInfo)
{
var status = ControllerStatus.IsController;
if (!typeInfo.IsClass)
{
status |= ControllerStatus.IsNotAClass;
}
if (typeInfo.IsAbstract)
{
status |= ControllerStatus.IsAbstract;
}
// We only consider public top-level classes as controllers. IsPublic returns false for nested
// classes, regardless of visibility modifiers
if (!typeInfo.IsPublic)
{
status |= ControllerStatus.IsNotPublicOrTopLevel;
}
if (typeInfo.ContainsGenericParameters)
{
status |= ControllerStatus.ContainsGenericParameters;
}
if (typeInfo.Name.Equals("Controller", StringComparison.OrdinalIgnoreCase))
{
status |= ControllerStatus.NameIsController;
}
if (!typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
!typeof(Controller).GetTypeInfo().IsAssignableFrom(typeInfo))
{
status |= ControllerStatus.DoesNotEndWithControllerAndIsNotAssignable;
}
if (_logger.IsEnabled(LogLevel.Verbose))
{
_logger.WriteVerbose(new IsControllerValues(
typeInfo.AsType(),
status));
}
return status == ControllerStatus.IsController;
}
/// <summary>
/// Creates an <see cref="ControllerModel"/> for the given <see cref="TypeInfo"/>.
/// </summary>

View File

@ -100,10 +100,10 @@ namespace Microsoft.AspNet.Mvc
[Activate]
public ActionBindingContext BindingContext { get; set; }
[Activate]
[FromServices]
public IModelMetadataProvider MetadataProvider { get; set; }
[Activate]
[FromServices]
public IUrlHelper Url { get; set; }
public IPrincipal User

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.Mvc.ApplicationModels;
using Microsoft.AspNet.Mvc.Filters;
using Microsoft.AspNet.Mvc.Logging;
@ -15,18 +14,18 @@ namespace Microsoft.AspNet.Mvc
public class ControllerActionDescriptorProvider : IActionDescriptorProvider
{
private readonly IControllerModelBuilder _applicationModelBuilder;
private readonly IAssemblyProvider _assemblyProvider;
private readonly IControllerTypeProvider _controllerTypeProvider;
private readonly IReadOnlyList<IFilter> _globalFilters;
private readonly IEnumerable<IApplicationModelConvention> _conventions;
private readonly ILogger _logger;
public ControllerActionDescriptorProvider([NotNull] IAssemblyProvider assemblyProvider,
public ControllerActionDescriptorProvider([NotNull] IControllerTypeProvider controllerTypeProvider,
[NotNull] IControllerModelBuilder applicationModelBuilder,
[NotNull] IGlobalFilterProvider globalFilters,
[NotNull] IOptions<MvcOptions> optionsAccessor,
[NotNull] ILoggerFactory loggerFactory)
{
_assemblyProvider = assemblyProvider;
_controllerTypeProvider = controllerTypeProvider;
_applicationModelBuilder = applicationModelBuilder;
_globalFilters = globalFilters.Filters;
_conventions = optionsAccessor.Options.Conventions;
@ -66,17 +65,7 @@ namespace Microsoft.AspNet.Mvc
applicationModel.Filters.Add(filter);
}
var assemblies = _assemblyProvider.CandidateAssemblies;
var types = assemblies.SelectMany(a => a.DefinedTypes);
if (_logger.IsEnabled(LogLevel.Verbose))
{
foreach (var assembly in assemblies)
{
_logger.WriteVerbose(new AssemblyValues(assembly));
}
}
foreach (var type in types)
foreach (var type in _controllerTypeProvider.ControllerTypes)
{
var controllerModel = _applicationModelBuilder.BuildControllerModel(type);
if (controllerModel != null)

View File

@ -38,7 +38,8 @@ namespace Microsoft.AspNet.Mvc
{
get
{
return GetCandidateLibraries().SelectMany(l => l.LoadableAssemblies).Select(Load);
return GetCandidateLibraries().SelectMany(l => l.LoadableAssemblies)
.Select(Load);
}
}

View File

@ -3,106 +3,26 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.Framework.DependencyInjection;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Represents the <see cref="IControllerActivator"/> that is registered by default.
/// <see cref="IControllerActivator"/> that uses type activation to create controllers.
/// </summary>
public class DefaultControllerActivator : IControllerActivator
{
private readonly Func<Type, PropertyActivator<ActionContext>[]> _getPropertiesToActivate;
private readonly IDictionary<Type, Func<ActionContext, object>> _valueAccessorLookup;
private readonly ConcurrentDictionary<Type, PropertyActivator<ActionContext>[]> _injectActions;
private static readonly Func<Type, ObjectFactory> _createControllerFactory =
type => ActivatorUtilities.CreateFactory(type, Type.EmptyTypes);
/// <summary>
/// Initializes a new instance of the DefaultControllerActivator class.
/// </summary>
public DefaultControllerActivator()
private readonly ConcurrentDictionary<Type, ObjectFactory> _controllerFactories =
new ConcurrentDictionary<Type, ObjectFactory>();
/// <inheritdoc />
public object Create([NotNull] ActionContext actionContext, [NotNull] Type controllerType)
{
_valueAccessorLookup = CreateValueAccessorLookup();
_injectActions = new ConcurrentDictionary<Type, PropertyActivator<ActionContext>[]>();
_getPropertiesToActivate = type =>
PropertyActivator<ActionContext>.GetPropertiesToActivate(type,
typeof(ActivateAttribute),
CreateActivateInfo);
}
/// <summary>
/// Activates the specified controller by using the specified action context.
/// </summary>
/// <param name="controller">The controller to activate.</param>
/// <param name="context">The context of the executing action.</param>
public virtual void Activate([NotNull] object controller, [NotNull] ActionContext context)
{
var controllerType = controller.GetType();
var controllerTypeInfo = controllerType.GetTypeInfo();
if (controllerTypeInfo.IsValueType)
{
var message = Resources.FormatValueTypesCannotBeActivated(GetType().FullName);
throw new InvalidOperationException(message);
}
var propertiesToActivate = _injectActions.GetOrAdd(controllerType,
_getPropertiesToActivate);
for (var i = 0; i < propertiesToActivate.Length; i++)
{
var activateInfo = propertiesToActivate[i];
activateInfo.Activate(controller, context);
}
}
protected virtual IDictionary<Type, Func<ActionContext, object>> CreateValueAccessorLookup()
{
var dictionary = new Dictionary<Type, Func<ActionContext, object>>
{
{ 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<IModelMetadataProvider>(),
context.ModelState);
}
},
{
typeof(ActionBindingContext),
(context) =>
{
var serviceProvider = context.HttpContext.RequestServices;
var accessor = serviceProvider.GetRequiredService<IScopedInstance<ActionBindingContext>>();
return accessor.Value;
}
}
};
return dictionary;
}
private PropertyActivator<ActionContext> CreateActivateInfo(
PropertyInfo property)
{
Func<ActionContext, object> valueAccessor;
if (!_valueAccessorLookup.TryGetValue(property.PropertyType, out valueAccessor))
{
valueAccessor = (actionContext) =>
{
var serviceProvider = actionContext.HttpContext.RequestServices;
return serviceProvider.GetRequiredService(property.PropertyType);
};
}
return new PropertyActivator<ActionContext>(property, valueAccessor);
var factory = _controllerFactories.GetOrAdd(controllerType, _createControllerFactory);
return factory(actionContext.HttpContext.RequestServices, arguments: null);
}
}
}

View File

@ -2,27 +2,43 @@
// 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;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Default implementation for <see cref="IControllerFactory"/>.
/// </summary>
public class DefaultControllerFactory : IControllerFactory
{
private readonly IServiceProvider _serviceProvider;
private readonly ITypeActivator _typeActivator;
private readonly IControllerActivator _controllerActivator;
private readonly IDictionary<Type, Func<ActionContext, object>> _valueAccessorLookup;
private readonly ConcurrentDictionary<Type, PropertyActivator<ActionContext>[]> _activateActions;
private readonly Func<Type, PropertyActivator<ActionContext>[]> _getPropertiesToActivate;
private readonly Func<Type, Func<ActionContext, object>> _getRequiredService = GetRequiredService;
public DefaultControllerFactory(IServiceProvider serviceProvider,
ITypeActivator typeActivator,
IControllerActivator controllerActivator)
/// <summary>
/// Initializes a new instance of <see cref="DefaultControllerFactory"/>.
/// </summary>
/// <param name="controllerActivator"><see cref="IControllerActivator"/> used to create controller
/// instances.</param>
public DefaultControllerFactory(IControllerActivator controllerActivator)
{
_serviceProvider = serviceProvider;
_typeActivator = typeActivator;
_controllerActivator = controllerActivator;
_valueAccessorLookup = CreateValueAccessorLookup();
_activateActions = new ConcurrentDictionary<Type, PropertyActivator<ActionContext>[]>();
_getPropertiesToActivate = GetPropertiesToActivate;
}
public object CreateController(ActionContext actionContext)
/// <inheritdoc />
public object CreateController([NotNull] ActionContext actionContext)
{
var actionDescriptor = actionContext.ActionDescriptor as ControllerActionDescriptor;
if (actionDescriptor == null)
@ -33,15 +49,14 @@ namespace Microsoft.AspNet.Mvc
nameof(actionContext));
}
var controller = _typeActivator.CreateInstance(
_serviceProvider,
actionDescriptor.ControllerTypeInfo.AsType());
_controllerActivator.Activate(controller, actionContext);
var controllerType = actionDescriptor.ControllerTypeInfo.AsType();
var controller = _controllerActivator.Create(actionContext, controllerType);
ActivateProperties(controller, actionContext);
return controller;
}
/// <inheritdoc />
public void ReleaseController(object controller)
{
var disposableController = controller as IDisposable;
@ -51,5 +66,111 @@ namespace Microsoft.AspNet.Mvc
disposableController.Dispose();
}
}
/// <summary>
/// Activates the specified controller using the specified action context.
/// </summary>
/// <param name="controller">The controller to activate.</param>
/// <param name="context">The context of the executing action.</param>
protected virtual void ActivateProperties([NotNull] object controller, [NotNull] ActionContext context)
{
var controllerType = controller.GetType();
var controllerTypeInfo = controllerType.GetTypeInfo();
if (controllerTypeInfo.IsValueType)
{
var message = Resources.FormatValueTypesCannotBeActivated(GetType().FullName);
throw new InvalidOperationException(message);
}
var propertiesToActivate = _activateActions.GetOrAdd(controllerType,
_getPropertiesToActivate);
for (var i = 0; i < propertiesToActivate.Length; i++)
{
var activateInfo = propertiesToActivate[i];
activateInfo.Activate(controller, context);
}
}
/// <summary>
/// Returns a <see cref="IDictionary{TKey, TValue}"/> of property types to delegates used to activate
/// controller properties annotated with <see cref="ActivateAttribute"/>.
/// </summary>
/// <returns>A dictionary containing the property type to activator delegate mapping.</returns>
/// <remarks>Override this method to provide custom activation behavior for controller properties
/// annotated with <see cref="ActivateAttribute"/>.</remarks>
protected virtual IDictionary<Type, Func<ActionContext, object>> CreateValueAccessorLookup()
{
var dictionary = new Dictionary<Type, Func<ActionContext, object>>
{
{ 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<IModelMetadataProvider>(),
context.ModelState);
}
},
{
typeof(ActionBindingContext),
(context) =>
{
var serviceProvider = context.HttpContext.RequestServices;
var accessor = serviceProvider.GetRequiredService<IScopedInstance<ActionBindingContext>>();
return accessor.Value;
}
}
};
return dictionary;
}
private PropertyActivator<ActionContext>[] GetPropertiesToActivate(Type type)
{
var activatorsForActivateProperties = PropertyActivator<ActionContext>.GetPropertiesToActivate(
type,
typeof(ActivateAttribute),
CreateActivateInfo);
var activatorsForFromServiceProperties = PropertyActivator<ActionContext>.GetPropertiesToActivate(
type,
typeof(FromServicesAttribute),
CreateFromServicesInfo);
return Enumerable.Concat(activatorsForActivateProperties, activatorsForFromServiceProperties)
.ToArray();
}
private PropertyActivator<ActionContext> CreateActivateInfo(
PropertyInfo property)
{
Func<ActionContext, object> 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<ActionContext>(property, valueAccessor);
}
private PropertyActivator<ActionContext> CreateFromServicesInfo(
PropertyInfo property)
{
var valueAccessor = _getRequiredService(property.PropertyType);
return new PropertyActivator<ActionContext>(property, valueAccessor);
}
private static Func<ActionContext, object> GetRequiredService(Type propertyType)
{
return actionContext => actionContext.HttpContext.RequestServices.GetRequiredService(propertyType);
}
}
}

View File

@ -0,0 +1,92 @@
// 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.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNet.Mvc.Logging;
using Microsoft.Framework.Logging;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// A <see cref="IControllerTypeProvider"/> that identifies controller types from assemblies
/// specified by the registered <see cref="IAssemblyProvider"/>.
/// </summary>
public class DefaultControllerTypeProvider : IControllerTypeProvider
{
private readonly IAssemblyProvider _assemblyProvider;
private readonly ILogger _logger;
/// <summary>
/// Initializes a new instance of <see cref="DefaultControllerTypeProvider"/>.
/// </summary>
/// <param name="assemblyProvider"><see cref="IAssemblyProvider"/> that provides assemblies to look for
/// controllers in.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
public DefaultControllerTypeProvider(IAssemblyProvider assemblyProvider,
ILoggerFactory loggerFactory)
{
_assemblyProvider = assemblyProvider;
_logger = loggerFactory.Create<DefaultControllerTypeProvider>();
}
/// <inheritdoc />
public virtual IEnumerable<TypeInfo> ControllerTypes
{
get
{
var assemblies = _assemblyProvider.CandidateAssemblies;
if (_logger.IsEnabled(LogLevel.Verbose))
{
foreach (var assembly in assemblies)
{
_logger.WriteVerbose(new AssemblyValues(assembly));
}
}
var types = assemblies.SelectMany(a => a.DefinedTypes);
return types.Where(IsController);
}
}
/// <summary>
/// Returns <c>true</c> if the <paramref name="typeInfo"/> is a controller. Otherwise <c>false</c>.
/// </summary>
/// <param name="typeInfo">The <see cref="TypeInfo"/>.</param>
/// <returns><c>true</c> if the <paramref name="typeInfo"/> is a controller. Otherwise <c>false</c>.</returns>
protected internal virtual bool IsController([NotNull] TypeInfo typeInfo)
{
if (!typeInfo.IsClass)
{
return false;
}
if (typeInfo.IsAbstract)
{
return false;
}
// We only consider public top-level classes as controllers. IsPublic returns false for nested
// classes, regardless of visibility modifiers
if (!typeInfo.IsPublic)
{
return false;
}
if (typeInfo.ContainsGenericParameters)
{
return false;
}
if (typeInfo.Name.Equals("Controller", StringComparison.OrdinalIgnoreCase))
{
return false;
}
if (!typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
!typeof(Controller).GetTypeInfo().IsAssignableFrom(typeInfo))
{
return false;
}
return true;
}
}
}

View File

@ -0,0 +1,21 @@
// 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.Collections.Generic;
using System.Reflection;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// A <see cref="IAssemblyProvider"/> with a fixed set of candidate assemblies.
/// </summary>
public class FixedSetAssemblyProvider : IAssemblyProvider
{
/// <summary>
/// Gets the list of candidate assemblies.
/// </summary>
public IList<Assembly> CandidateAssemblies { get; } = new List<Assembly>();
IEnumerable<Assembly> IAssemblyProvider.CandidateAssemblies => CandidateAssemblies;
}
}

View File

@ -0,0 +1,39 @@
// 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.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// A <see cref="IControllerTypeProvider"/> with a fixed set of types that are used as controllers.
/// </summary>
public class FixedSetControllerTypeProvider : IControllerTypeProvider
{
/// <summary>
/// Initializes a new instance of <see cref="FixedSetControllerTypeProvider"/>.
/// </summary>
public FixedSetControllerTypeProvider()
: this(Enumerable.Empty<TypeInfo>())
{
}
/// <summary>
/// Initializes a new instance of <see cref="FixedSetControllerTypeProvider"/>.
/// </summary>
/// <param name="controllerTypes">The sequence of controller <see cref="TypeInfo"/>.</param>
public FixedSetControllerTypeProvider([NotNull] IEnumerable<TypeInfo> controllerTypes)
{
ControllerTypes = new List<TypeInfo>(controllerTypes);
}
/// <summary>
/// Gets the list of controller <see cref="TypeInfo"/>s.
/// </summary>
public IList<TypeInfo> ControllerTypes { get; }
IEnumerable<TypeInfo> IControllerTypeProvider.ControllerTypes => ControllerTypes;
}
}

View File

@ -6,8 +6,16 @@ using System.Reflection;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Specifies the contract for discovering assemblies that may contain Mvc specific types such as controllers,
/// view components and precompiled views.
/// </summary>
public interface IAssemblyProvider
{
/// <summary>
/// Gets the sequence of candidate <see cref="Assembly"/>ies that the application
/// uses for discovery of Mvc specific types.
/// </summary>
IEnumerable<Assembly> CandidateAssemblies { get; }
}
}

View File

@ -1,18 +1,19 @@
// 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;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Provides methods to activate an instantiated controller.
/// Provides methods to create a controller.
/// </summary>
public interface IControllerActivator
{
/// <summary>
/// When implemented in a type, activates an instantiated controller.
/// Creates a controller.
/// </summary>
/// <param name="controller">The controller to activate.</param>
/// <param name="context">The <see cref="ActionContext"/> for the executing action.</param>
void Activate(object controller, ActionContext context);
object Create(ActionContext context, Type controllerType);
}
}

View File

@ -3,9 +3,22 @@
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Provides methods for creation and disposal of controllers.
/// </summary>
public interface IControllerFactory
{
/// <summary>
/// Creates a new controller for the specified <paramref name="actionContext"/>.
/// </summary>
/// <param name="actionContext"><see cref="ActionContext"/> for the action to execute.</param>
/// <returns>The controller.</returns>
object CreateController(ActionContext actionContext);
/// <summary>
/// Releases a controller instance.
/// </summary>
/// <param name="controller">The controller.</param>
void ReleaseController(object controller);
}
}

View File

@ -0,0 +1,19 @@
// 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.Collections.Generic;
using System.Reflection;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Provides methods for discovery of controller types.
/// </summary>
public interface IControllerTypeProvider
{
/// <summary>
/// Gets a <see cref="IEnumerable{T}"/> of controller <see cref="TypeInfo"/>s.
/// </summary>
IEnumerable<TypeInfo> ControllerTypes { get; }
}
}

View File

@ -1,24 +0,0 @@
// 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;
namespace Microsoft.AspNet.Mvc.Logging
{
/// <summary>
/// Indicates the status of a class during controller discovery.
/// All values except 0 represent a reason why a type is not a controller.
/// </summary>
[Flags]
public enum ControllerStatus
{
IsController = 0,
IsNotAClass = 1,
IsNotPublicOrTopLevel = 2,
IsAbstract = 4,
ContainsGenericParameters = 8,
// The name of the controller class is "Controller"
NameIsController = 16,
DoesNotEndWithControllerAndIsNotAssignable = 32
}
}

View File

@ -1,36 +0,0 @@
// 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 Microsoft.Framework.Logging;
namespace Microsoft.AspNet.Mvc.Logging
{
/// <summary>
/// Logged to indicate the state of a class during controller discovery. Logs the type
/// of the controller as well as the <see cref="ControllerStatus"/>.
/// </summary>
public class IsControllerValues : LoggerStructureBase
{
public IsControllerValues(Type type, ControllerStatus status)
{
Type = type;
Status = status;
}
/// <summary>
/// The <see cref="System.Type"/> of the potential <see cref="Controller"/> class.
/// </summary>
public Type Type { get; }
/// <summary>
/// The <see cref="ControllerStatus"/> of the <see cref="Type"/>.
/// </summary>
public ControllerStatus Status { get; }
public override string Format()
{
return LogFormatter.FormatStructure(this);
}
}
}

View File

@ -1642,6 +1642,22 @@ namespace Microsoft.AspNet.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("Format_NotValid"), p0);
}
/// <summary>
/// The property '{0}' on controller '{1}' cannot be activated.
/// </summary>
internal static string ControllerFactory_PropertyCannotBeActivated
{
get { return GetString("ControllerFactory_PropertyCannotBeActivated"); }
}
/// <summary>
/// The property '{0}' on controller '{1}' cannot be activated.
/// </summary>
internal static string FormatControllerFactory_PropertyCannotBeActivated(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("ControllerFactory_PropertyCannotBeActivated"), p0, p1);
}
/// <summary>
/// No URL for remote validation could be found.
/// </summary>

View File

@ -433,6 +433,9 @@
<data name="Format_NotValid" xml:space="preserve">
<value>The format provided is invalid '{0}'. A format must be a non-empty file-extension, optionally prefixed with a '.' character.</value>
</data>
<data name="ControllerFactory_PropertyCannotBeActivated" xml:space="preserve">
<value>The property '{0}' on controller '{1}' cannot be activated.</value>
</data>
<data name="RemoteAttribute_NoUrlFound" xml:space="preserve">
<value>No URL for remote validation could be found.</value>
</data>

View File

@ -0,0 +1,21 @@
// 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 Microsoft.Framework.DependencyInjection;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// A <see cref="IControllerActivator"/> that retrieves controllers as services from the request's
/// <see cref="IServiceProvider"/>.
/// </summary>
public class ServiceBasedControllerActivator : IControllerActivator
{
/// <inheritdoc />
public object Create([NotNull] ActionContext actionContext, [NotNull] Type controllerType)
{
return actionContext.HttpContext.RequestServices.GetRequiredService(controllerType);
}
}
}

View File

@ -51,7 +51,7 @@ namespace System.Web.Http
/// Gets the <see cref="IModelMetadataProvider"/>.
/// </summary>
/// <remarks>The setter is intended for unit testing purposes only.</remarks>
[Activate]
[FromServices]
public IModelMetadataProvider MetadataProvider { get; set; }
/// <summary>
@ -91,7 +91,7 @@ namespace System.Web.Http
/// Gets a factory used to generate URLs to other APIs.
/// </summary>
/// <remarks>The setter is intended for unit testing purposes only.</remarks>
[Activate]
[FromServices]
public IUrlHelper Url { get; set; }
/// <summary>

View File

@ -2,9 +2,13 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.ConfigurationModel;
using Microsoft.Framework.Logging;
namespace Microsoft.Framework.DependencyInjection
{
@ -36,6 +40,92 @@ namespace Microsoft.Framework.DependencyInjection
services.Configure(setupAction);
}
/// <summary>
/// Register the specified <paramref name="controllerTypes"/> as services and as a source for controller
/// discovery.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
/// <param name="controllerTypes">A sequence of controller <see cref="Type"/>s to register in the <paramref name="services"/>
/// and used for controller discovery.</param>
/// <returns>The <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection WithControllersAsServices(
[NotNull] this IServiceCollection services,
[NotNull] IEnumerable<Type> controllerTypes)
{
return WithControllersAsServices(services, controllerTypes, configuration: null);
}
/// <summary>
/// Register the specified <paramref name="controllerTypes"/> as services and as a source for controller
/// discovery.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
/// <param name="controllerTypes">A sequence of controller <see cref="Type"/>s to register
/// in the <paramref name="services"/> and used for controller discovery.</param>
/// <param name="configuration">The application's <see cref="IConfiguration"/>.</param>
/// <returns>The <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection WithControllersAsServices(
[NotNull] this IServiceCollection services,
[NotNull] IEnumerable<Type> controllerTypes,
IConfiguration configuration)
{
var controllerTypeProvider = new FixedSetControllerTypeProvider();
foreach (var type in controllerTypes)
{
services.AddTransient(type);
controllerTypeProvider.ControllerTypes.Add(type.GetTypeInfo());
}
var describer = new ServiceDescriber(configuration);
services.Replace(describer.Transient<IControllerActivator, ServiceBasedControllerActivator>());
services.Replace(describer.Instance<IControllerTypeProvider>(controllerTypeProvider));
return services;
}
/// <summary>
/// Registers controller types from the specified <paramref name="assemblies"/> as services and as a source
/// for controller discovery.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
/// <param name="controllerAssemblies">Assemblies to scan.</param>
/// <returns>The <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection WithControllersAsServices(
[NotNull] this IServiceCollection services,
[NotNull] IEnumerable<Assembly> controllerAssemblies)
{
return WithControllersAsServices(services,
controllerAssemblies,
configuration: null);
}
/// <summary>
/// Registers controller types from the specified <paramref name="assemblies"/> as services and as a source
/// for controller discovery.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
/// <param name="controllerAssemblies">Assemblies to scan.</param>
/// <param name="configuration">The application's <see cref="IConfiguration"/>.</param>
/// <returns>The <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection WithControllersAsServices(
[NotNull] this IServiceCollection services,
[NotNull] IEnumerable<Assembly> controllerAssemblies,
IConfiguration configuration)
{
var assemblyProvider = new FixedSetAssemblyProvider();
foreach (var assembly in controllerAssemblies)
{
assemblyProvider.CandidateAssemblies.Add(assembly);
}
var loggerFactory = NullLoggerFactory.Instance;
var controllerTypeProvider = new DefaultControllerTypeProvider(assemblyProvider, loggerFactory);
var controllerTypes = controllerTypeProvider.ControllerTypes;
return WithControllersAsServices(services,
controllerTypes.Select(type => type.AsType()),
configuration);
}
private static void ConfigureDefaultServices(IServiceCollection services, IConfiguration configuration)
{
services.AddOptions(configuration);

View File

@ -42,11 +42,12 @@ namespace Microsoft.AspNet.Mvc
// Core action discovery, filters and action execution.
// These are consumed only when creating action descriptors, then they can be de-allocated
yield return describe.Transient<IControllerTypeProvider, DefaultControllerTypeProvider>();
yield return describe.Transient<IControllerModelBuilder, DefaultControllerModelBuilder>();
yield return describe.Transient<IActionModelBuilder, DefaultActionModelBuilder>();
// This accesses per-request services to activate the controller
yield return describe.Transient<IControllerFactory, DefaultControllerFactory>();
// This has a cache, so it needs to be a singleton
yield return describe.Singleton<IControllerFactory, DefaultControllerFactory>();
// This has a cache, so it needs to be a singleton
yield return describe.Singleton<IControllerActivator, DefaultControllerActivator>();

View File

@ -1,176 +1,22 @@
// 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.Reflection;
using Microsoft.AspNet.Mvc.Filters;
using Microsoft.AspNet.Mvc.ApplicationModels.DefaultControllerModelBuilderTestControllers;
using Xunit;
using System;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Filters;
using Xunit;
namespace Microsoft.AspNet.Mvc.ApplicationModels
{
public class DefaultControllerModelBuilderTest
{
[Fact]
public void IsController_UserDefinedClass()
{
// Arrange
var builder = new AccessibleControllerModelBuilder();
var typeInfo = typeof(StoreController).GetTypeInfo();
// Act
var isController = builder.IsController(typeInfo);
// Assert
Assert.True(isController);
}
[Fact]
public void IsController_FrameworkControllerClass()
{
// Arrange
var builder = new AccessibleControllerModelBuilder();
var typeInfo = typeof(Controller).GetTypeInfo();
// Act
var isController = builder.IsController(typeInfo);
// Assert
Assert.False(isController);
}
[Fact]
public void IsController_UserDefinedControllerClass()
{
// Arrange
var builder = new AccessibleControllerModelBuilder();
var typeInfo = typeof(DefaultControllerModelBuilderTestControllers.Controller).GetTypeInfo();
// Act
var isController = builder.IsController(typeInfo);
// Assert
Assert.False(isController);
}
[Fact]
public void IsController_Interface()
{
// Arrange
var builder = new AccessibleControllerModelBuilder();
var typeInfo = typeof(IController).GetTypeInfo();
// Act
var isController = builder.IsController(typeInfo);
// Assert
Assert.False(isController);
}
[Fact]
public void IsController_AbstractClass()
{
// Arrange
var builder = new AccessibleControllerModelBuilder();
var typeInfo = typeof(AbstractController).GetTypeInfo();
// Act
var isController = builder.IsController(typeInfo);
// Assert
Assert.False(isController);
}
[Fact]
public void IsController_DerivedAbstractClass()
{
// Arrange
var builder = new AccessibleControllerModelBuilder();
var typeInfo = typeof(DerivedAbstractController).GetTypeInfo();
// Act
var isController = builder.IsController(typeInfo);
// Assert
Assert.True(isController);
}
[Fact]
public void IsController_OpenGenericClass()
{
// Arrange
var builder = new AccessibleControllerModelBuilder();
var typeInfo = typeof(OpenGenericController<>).GetTypeInfo();
// Act
var isController = builder.IsController(typeInfo);
// Assert
Assert.False(isController);
}
[Fact]
public void IsController_ClosedGenericClass()
{
// Arrange
var builder = new AccessibleControllerModelBuilder();
var typeInfo = typeof(OpenGenericController<string>).GetTypeInfo();
// Act
var isController = builder.IsController(typeInfo);
// Assert
Assert.True(isController);
}
[Fact]
public void IsController_DerivedGenericClass()
{
// Arrange
var builder = new AccessibleControllerModelBuilder();
var typeInfo = typeof(DerivedGenericController).GetTypeInfo();
// Act
var isController = builder.IsController(typeInfo);
// Assert
Assert.True(isController);
}
[Fact]
public void IsController_Poco_WithNamingConvention()
{
// Arrange
var builder = new AccessibleControllerModelBuilder();
var typeInfo = typeof(PocoController).GetTypeInfo();
// Act
var isController = builder.IsController(typeInfo);
// Assert
Assert.True(isController);
}
[Fact]
public void IsController_NoControllerSuffix()
{
// Arrange
var builder = new AccessibleControllerModelBuilder();
var typeInfo = typeof(NoSuffix).GetTypeInfo();
// Act
var isController = builder.IsController(typeInfo);
// Assert
Assert.True(isController);
}
[Fact]
public void BuildControllerModel_DerivedFromControllerClass_HasFilter()
{
// Arrange
var builder = new AccessibleControllerModelBuilder();
var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
var typeInfo = typeof(StoreController).GetTypeInfo();
// Act
@ -187,7 +33,8 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void BuildControllerModel_ClassWithoutFilterInterfaces_HasNoControllerFilter()
{
// Arrange
var builder = new AccessibleControllerModelBuilder();
var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
var typeInfo = typeof(NoFiltersController).GetTypeInfo();
// Act
@ -202,7 +49,8 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void BuildControllerModel_ClassWithFilterInterfaces_HasFilter()
{
// Arrange
var builder = new AccessibleControllerModelBuilder();
var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
var typeInfo = typeof(SomeFiltersController).GetTypeInfo();
// Act
@ -217,7 +65,8 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void BuildControllerModel_ClassWithFilterInterfaces_UnsupportedType()
{
// Arrange
var builder = new AccessibleControllerModelBuilder();
var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
var typeInfo = typeof(UnsupportedFiltersController).GetTypeInfo();
// Act
@ -227,102 +76,49 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
Assert.Empty(model.Filters);
}
private class AccessibleControllerModelBuilder : DefaultControllerModelBuilder
private class StoreController : Mvc.Controller
{
public AccessibleControllerModelBuilder()
: base(new DefaultActionModelBuilder(), new NullLoggerFactory())
}
[Produces("application/json")]
private class NoFiltersController
{
}
private class SomeFiltersController : IAsyncActionFilter, IResultFilter
{
public Task OnActionExecutionAsync(
[NotNull] ActionExecutingContext context,
[NotNull] ActionExecutionDelegate next)
{
return null;
}
public void OnResultExecuted([NotNull] ResultExecutedContext context)
{
}
public new bool IsController([NotNull]TypeInfo typeInfo)
public void OnResultExecuting([NotNull]ResultExecutingContext context)
{
return base.IsController(typeInfo);
}
}
private class UnsupportedFiltersController : IExceptionFilter, IAuthorizationFilter, IAsyncResourceFilter
{
public void OnAuthorization([NotNull]AuthorizationContext context)
{
throw new NotImplementedException();
}
public void OnException([NotNull]ExceptionContext context)
{
throw new NotImplementedException();
}
public Task OnResourceExecutionAsync([NotNull]ResourceExecutingContext context, [NotNull]ResourceExecutionDelegate next)
{
throw new NotImplementedException();
}
}
}
}
// These controllers are used to test the DefaultActionDiscoveryConventions implementation
// which REQUIRES that they be public top-level classes. To avoid having to stub out the
// implementation of this class to test it, they are just top level classes. Don't reuse
// these outside this test - find a better way or use nested classes to keep the tests
// independent.
namespace Microsoft.AspNet.Mvc.ApplicationModels.DefaultControllerModelBuilderTestControllers
{
public abstract class AbstractController : Mvc.Controller
{
}
public class DerivedAbstractController : AbstractController
{
}
public class StoreController : Mvc.Controller
{
}
public class Controller
{
}
public class OpenGenericController<T> : Mvc.Controller
{
}
public class DerivedGenericController : OpenGenericController<string>
{
}
public interface IController
{
}
public class NoSuffix : Mvc.Controller
{
}
public class PocoController
{
}
[Produces("application/json")]
public class NoFiltersController
{
}
public class SomeFiltersController : IAsyncActionFilter, IResultFilter
{
public Task OnActionExecutionAsync(
[NotNull] ActionExecutingContext context,
[NotNull] ActionExecutionDelegate next)
{
return null;
}
public void OnResultExecuted([NotNull] ResultExecutedContext context)
{
}
public void OnResultExecuting([NotNull ]ResultExecutingContext context)
{
}
}
public class UnsupportedFiltersController : IExceptionFilter, IAuthorizationFilter, IAsyncResourceFilter
{
public void OnAuthorization([NotNull]AuthorizationContext context)
{
throw new NotImplementedException();
}
public void OnException([NotNull]ExceptionContext context)
{
throw new NotImplementedException();
}
public Task OnResourceExecutionAsync([NotNull]ResourceExecutingContext context, [NotNull]ResourceExecutionDelegate next)
{
throw new NotImplementedException();
}
}
}

View File

@ -1362,16 +1362,13 @@ namespace Microsoft.AspNet.Mvc.Test
TypeInfo controllerTypeInfo,
IEnumerable<IFilter> filters = null)
{
var modelBuilder = new StaticControllerModelBuilder(controllerTypeInfo);
var assemblyProvider = new Mock<IAssemblyProvider>();
assemblyProvider
.SetupGet(ap => ap.CandidateAssemblies)
.Returns(new Assembly[] { controllerTypeInfo.Assembly });
var controllerTypeProvider = new FixedSetControllerTypeProvider(new[] { controllerTypeInfo });
var controllerModelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
var provider = new ControllerActionDescriptorProvider(
assemblyProvider.Object,
modelBuilder,
controllerTypeProvider,
controllerModelBuilder,
new TestGlobalFilterProvider(filters),
new MockMvcOptionsAccessor(),
new NullLoggerFactory());
@ -1382,16 +1379,13 @@ namespace Microsoft.AspNet.Mvc.Test
private ControllerActionDescriptorProvider GetProvider(
params TypeInfo[] controllerTypeInfo)
{
var modelBuilder = new StaticControllerModelBuilder(controllerTypeInfo);
var assemblyProvider = new Mock<IAssemblyProvider>();
assemblyProvider
.SetupGet(ap => ap.CandidateAssemblies)
.Returns(new Assembly[] { controllerTypeInfo.First().Assembly });
var controllerTypeProvider = new FixedSetControllerTypeProvider(controllerTypeInfo);
var controllerModelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
var provider = new ControllerActionDescriptorProvider(
assemblyProvider.Object,
modelBuilder,
controllerTypeProvider,
controllerModelBuilder,
new TestGlobalFilterProvider(),
new MockMvcOptionsAccessor(),
new NullLoggerFactory());
@ -1403,18 +1397,15 @@ namespace Microsoft.AspNet.Mvc.Test
TypeInfo type,
IApplicationModelConvention convention)
{
var modelBuilder = new StaticControllerModelBuilder(type);
var assemblyProvider = new Mock<IAssemblyProvider>();
assemblyProvider
.SetupGet(ap => ap.CandidateAssemblies)
.Returns(new Assembly[] { type.Assembly });
var controllerTypeProvider = new FixedSetControllerTypeProvider(new[] { type });
var modelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
var options = new MockMvcOptionsAccessor();
options.Options.Conventions.Add(convention);
return new ControllerActionDescriptorProvider(
assemblyProvider.Object,
controllerTypeProvider,
modelBuilder,
new TestGlobalFilterProvider(),
options,
@ -1423,15 +1414,12 @@ namespace Microsoft.AspNet.Mvc.Test
private IEnumerable<ActionDescriptor> GetDescriptors(params TypeInfo[] controllerTypeInfos)
{
var modelBuilder = new StaticControllerModelBuilder(controllerTypeInfos);
var assemblyProvider = new Mock<IAssemblyProvider>();
assemblyProvider
.SetupGet(ap => ap.CandidateAssemblies)
.Returns(controllerTypeInfos.Select(cti => cti.Assembly).Distinct());
var controllerTypeProvider = new FixedSetControllerTypeProvider(controllerTypeInfos);
var modelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
var provider = new ControllerActionDescriptorProvider(
assemblyProvider.Object,
controllerTypeProvider,
modelBuilder,
new TestGlobalFilterProvider(),
new MockMvcOptionsAccessor(),

View File

@ -8,32 +8,12 @@ using Microsoft.AspNet.Mvc.ApplicationModels;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.NestedProviders;
using Microsoft.Framework.Logging;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.Logging
{
public class DefaultActionDescriptorCollectionProviderLoggingTest
{
[Fact]
public void SimpleController_AssemblyDiscovery()
{
// Arrange
var sink = new TestSink();
var loggerFactory = new TestLoggerFactory(sink);
// Act
var provider = GetProvider(loggerFactory, typeof(SimpleController).GetTypeInfo());
provider.BuildModel();
// Assert
Assert.Single(sink.Writes);
var assemblyValues = sink.Writes[0].State as AssemblyValues;
Assert.NotNull(assemblyValues);
Assert.True(assemblyValues.AssemblyName.Contains("Microsoft.AspNet.Mvc.Core.Test"));
}
[Fact]
public void ControllerDiscovery()
{
@ -49,10 +29,10 @@ namespace Microsoft.AspNet.Mvc.Logging
provider.GetDescriptors();
// Assert
// 1 assembly, 2 controllers
Assert.Equal(3, sink.Writes.Count);
// 2 controllers
Assert.Equal(2, sink.Writes.Count);
var controllerModelValues = sink.Writes[1].State as ControllerModelValues;
var controllerModelValues = Assert.IsType<ControllerModelValues>(sink.Writes[0].State);
Assert.NotNull(controllerModelValues);
Assert.Equal("Simple", controllerModelValues.ControllerName);
Assert.Equal(typeof(SimpleController), controllerModelValues.ControllerType);
@ -62,7 +42,7 @@ namespace Microsoft.AspNet.Mvc.Logging
Assert.Empty(controllerModelValues.Attributes);
Assert.Empty(controllerModelValues.Filters);
controllerModelValues = sink.Writes[2].State as ControllerModelValues;
controllerModelValues = Assert.IsType<ControllerModelValues>(sink.Writes[1].State);
Assert.NotNull(controllerModelValues);
Assert.Equal("Basic", controllerModelValues.ControllerName);
Assert.Equal(typeof(BasicController), controllerModelValues.ControllerType);
@ -88,10 +68,12 @@ namespace Microsoft.AspNet.Mvc.Logging
typeof(BasicController).GetTypeInfo());
// Assert
// 1 assembly, 2 controllers, 3 actions
Assert.Equal(6, sink.Writes.Count);
// 2 controllers, 3 actions
Assert.Equal(5, sink.Writes.Count);
Assert.IsType<ControllerModelValues>(sink.Writes[0].State);
Assert.IsType<ControllerModelValues>(sink.Writes[1].State);
var actionDescriptorValues = sink.Writes[3].State as ActionDescriptorValues;
var actionDescriptorValues = Assert.IsType<ActionDescriptorValues>(sink.Writes[2].State);
Assert.NotNull(actionDescriptorValues);
Assert.Equal("EmptyAction", actionDescriptorValues.Name);
Assert.Equal("Simple", actionDescriptorValues.ControllerName);
@ -101,7 +83,7 @@ namespace Microsoft.AspNet.Mvc.Logging
Assert.Empty(actionDescriptorValues.FilterDescriptors);
Assert.Empty(actionDescriptorValues.Parameters);
actionDescriptorValues = sink.Writes[4].State as ActionDescriptorValues;
actionDescriptorValues = Assert.IsType<ActionDescriptorValues>(sink.Writes[3].State);
Assert.NotNull(actionDescriptorValues);
Assert.Equal("Basic", actionDescriptorValues.Name);
Assert.Equal("Basic", actionDescriptorValues.ControllerName);
@ -111,7 +93,7 @@ namespace Microsoft.AspNet.Mvc.Logging
Assert.Equal(2, actionDescriptorValues.FilterDescriptors.Count);
Assert.Empty(actionDescriptorValues.Parameters);
actionDescriptorValues = sink.Writes[5].State as ActionDescriptorValues;
actionDescriptorValues = Assert.IsType<ActionDescriptorValues>(sink.Writes[4].State);
Assert.NotNull(actionDescriptorValues);
Assert.Equal("Basic", actionDescriptorValues.Name);
Assert.Equal("Basic", actionDescriptorValues.ControllerName);
@ -140,15 +122,12 @@ namespace Microsoft.AspNet.Mvc.Logging
private ControllerActionDescriptorProvider GetProvider(
ILoggerFactory loggerFactory, params TypeInfo[] controllerTypeInfo)
{
var modelBuilder = new StaticControllerModelBuilder(controllerTypeInfo);
var assemblyProvider = new Mock<IAssemblyProvider>();
assemblyProvider
.SetupGet(ap => ap.CandidateAssemblies)
.Returns(new Assembly[] { controllerTypeInfo.First().Assembly });
var controllerTypeProvider = new FixedSetControllerTypeProvider(controllerTypeInfo);
var modelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
loggerFactory);
var provider = new ControllerActionDescriptorProvider(
assemblyProvider.Object,
controllerTypeProvider,
modelBuilder,
new TestGlobalFilterProvider(),
new MockMvcOptionsAccessor(),

View File

@ -759,16 +759,17 @@ namespace Microsoft.AspNet.Mvc
private ControllerActionDescriptorProvider GetActionDescriptorProvider()
{
var assemblyProvider = new StaticAssemblyProvider();
var controllerTypes = typeof(DefaultActionSelectorTests)
.GetNestedTypes(BindingFlags.NonPublic)
.Select(t => t.GetTypeInfo());
.Select(t => t.GetTypeInfo())
.ToList();
var modelBuilder = new StaticControllerModelBuilder(controllerTypes.ToArray());
var controllerTypeProvider = new FixedSetControllerTypeProvider(controllerTypes);
var modelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
return new ControllerActionDescriptorProvider(
assemblyProvider,
controllerTypeProvider,
modelBuilder,
new TestGlobalFilterProvider(),
new MockMvcOptionsAccessor(),

View File

@ -1,182 +1,79 @@
// 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.
#if ASPNET50
using System;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.Core.Test
namespace Microsoft.AspNet.Mvc
{
public class DefaultControllerActivatorTest
{
[Fact]
public void Activate_SetsPropertiesFromActionContextHierarchy()
[Theory]
[InlineData(typeof(TypeDerivingFromController))]
[InlineData(typeof(PocoType))]
public void Create_CreatesInstancesOfTypes(Type type)
{
// Arrange
var services = GetServices();
var httpRequest = Mock.Of<HttpRequest>();
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(c => c.Request)
.Returns(httpRequest);
httpContext.SetupGet(c => c.RequestServices)
.Returns(services);
var controller = new TestController();
var context = new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor());
var activator = new DefaultControllerActivator();
var actionContext = new ActionContext(new DefaultHttpContext(),
new RouteData(),
new ActionDescriptor());
// Act
activator.Activate(controller, context);
var instance = activator.Create(actionContext, type);
// Assert
Assert.Same(context, controller.ActionContext);
Assert.Same(httpContext.Object, controller.HttpContext);
Assert.Same(httpRequest, controller.GetHttpRequest());
Assert.IsType(type, instance);
}
[Fact]
public void Activate_SetsViewDatDictionary()
public void Create_TypeActivatesTypesWithServices()
{
// Arrange
var services = GetServices();
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(c => c.RequestServices)
.Returns(services);
var controller = new TestController();
var context = new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor());
var activator = new DefaultControllerActivator();
// Act
activator.Activate(controller, context);
// Assert
Assert.NotNull(controller.GetViewData());
}
[Fact]
public void Activate_SetsBindingContext()
{
// Arrange
var bindingContext = new ActionBindingContext();
var services = GetServices();
services.GetRequiredService<IScopedInstance<ActionBindingContext>>().Value = bindingContext;
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(c => c.RequestServices)
.Returns(services);
var controller = new TestController();
var context = new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor());
var activator = new DefaultControllerActivator();
// Act
activator.Activate(controller, context);
// Assert
Assert.Same(bindingContext, controller.BindingContext);
}
[Fact]
public void Activate_PopulatesServicesFromServiceContainer()
{
// Arrange
var services = GetServices();
var urlHelper = services.GetRequiredService<IUrlHelper>();
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(c => c.RequestServices)
.Returns(services);
var controller = new TestController();
var context = new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor());
var activator = new DefaultControllerActivator();
// Act
activator.Activate(controller, context);
// Assert
Assert.Same(urlHelper, controller.Helper);
}
[Fact]
public void Activate_IgnoresPropertiesThatAreNotDecoratedWithActivateAttribute()
{
// Arrange
var services = GetServices();
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(c => c.Response)
.Returns(Mock.Of<HttpResponse>());
httpContext.SetupGet(c => c.RequestServices)
.Returns(services);
var controller = new TestController();
var context = new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor());
var activator = new DefaultControllerActivator();
// Act
activator.Activate(controller, context);
// Assert
Assert.Null(controller.Response);
}
private IServiceProvider GetServices()
{
var services = new Mock<IServiceProvider>();
services.Setup(s => s.GetService(typeof(IUrlHelper)))
.Returns(Mock.Of<IUrlHelper>());
services.Setup(s => s.GetService(typeof(IModelMetadataProvider)))
.Returns(new EmptyModelMetadataProvider());
services
.Setup(s => s.GetService(typeof(IScopedInstance<ActionBindingContext>)))
.Returns(new MockScopedInstance<ActionBindingContext>());
return services.Object;
}
public class TestController
{
[Activate]
public ActionContext ActionContext { get; set; }
[Activate]
public ActionBindingContext BindingContext { get; set; }
[Activate]
public HttpContext HttpContext { get; set; }
[Activate]
protected HttpRequest Request { get; set; }
[Activate]
private ViewDataDictionary ViewData { get; set; }
[Activate]
public IUrlHelper Helper { get; set; }
public HttpResponse Response { get; set; }
public ViewDataDictionary GetViewData()
var serviceProvider = new Mock<IServiceProvider>(MockBehavior.Strict);
var testService = new TestService();
serviceProvider.Setup(s => s.GetService(typeof(TestService)))
.Returns(testService)
.Verifiable();
var httpContext = new DefaultHttpContext
{
return ViewData;
RequestServices = serviceProvider.Object
};
var actionContext = new ActionContext(httpContext,
new RouteData(),
new ActionDescriptor());
// Act
var instance = activator.Create(actionContext, typeof(TypeDerivingFromControllerWithServices));
// Assert
var controller = Assert.IsType<TypeDerivingFromControllerWithServices>(instance);
Assert.Same(testService, controller.TestService);
serviceProvider.Verify();
}
private class TypeDerivingFromController : Controller
{
}
private class TypeDerivingFromControllerWithServices : Controller
{
public TypeDerivingFromControllerWithServices(TestService service)
{
TestService = service;
}
public HttpRequest GetHttpRequest()
{
return Request;
}
public TestService TestService { get; }
}
private class PocoType
{
}
private class TestService
{
}
}
}
#endif

View File

@ -2,23 +2,249 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Reflection;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.Core.Test
namespace Microsoft.AspNet.Mvc.Core
{
public class DefaultControllerFactoryTest
{
[Fact]
public void CreateController_ThrowsIfActionDescriptorIsNotControllerActionDescriptor()
{
// Arrange
var expected = "The action descriptor must be of type 'Microsoft.AspNet.Mvc.ControllerActionDescriptor'." +
Environment.NewLine + "Parameter name: actionContext";
var actionDescriptor = new ActionDescriptor();
var controllerFactory = new DefaultControllerFactory(Mock.Of<IControllerActivator>());
var httpContext = new DefaultHttpContext();
var actionContext = new ActionContext(httpContext,
new RouteData(),
actionDescriptor);
// Act and Assert
var ex = Assert.Throws<ArgumentException>(() =>
controllerFactory.CreateController(actionContext));
Assert.Equal(expected, ex.Message);
Assert.Equal("actionContext", ex.ParamName);
}
[Fact]
public void CreateController_UsesControllerActivatorToInstantiateController()
{
// Arrange
var expected = new MyController();
var actionDescriptor = new ControllerActionDescriptor
{
ControllerTypeInfo = typeof(MyController).GetTypeInfo()
};
var httpContext = new DefaultHttpContext();
httpContext.RequestServices = GetServices();
var actionContext = new ActionContext(httpContext,
new RouteData(),
actionDescriptor);
var activator = new Mock<IControllerActivator>();
activator.Setup(a => a.Create(actionContext, typeof(MyController)))
.Returns(expected)
.Verifiable();
var controllerFactory = new DefaultControllerFactory(activator.Object);
// Act
var result = controllerFactory.CreateController(actionContext);
// Assert
var controller = Assert.IsType<MyController>(result);
Assert.Same(expected, controller);
activator.Verify();
}
[Fact]
public void CreateController_SetsPropertiesFromActionContextHierarchy()
{
// Arrange
var actionDescriptor = new ControllerActionDescriptor
{
ControllerTypeInfo = typeof(ControllerWithActivateAndFromServices).GetTypeInfo()
};
var services = GetServices();
var httpContext = new DefaultHttpContext
{
RequestServices = services
};
var context = new ActionContext(httpContext, new RouteData(), actionDescriptor);
var factory = new DefaultControllerFactory(new DefaultControllerActivator());
// Act
var result = factory.CreateController(context);
// Assert
var controller = Assert.IsType<ControllerWithActivateAndFromServices>(result);
Assert.Same(context, controller.ActionContext);
Assert.Same(httpContext, controller.HttpContext);
}
[Fact]
public void CreateController_SetsViewDataDictionary()
{
// Arrange
var actionDescriptor = new ControllerActionDescriptor
{
ControllerTypeInfo = typeof(ControllerWithActivateAndFromServices).GetTypeInfo()
};
var services = GetServices();
var httpContext = new DefaultHttpContext
{
RequestServices = services
};
var context = new ActionContext(httpContext, new RouteData(), actionDescriptor);
var factory = new DefaultControllerFactory(new DefaultControllerActivator());
// Act
var result = factory.CreateController(context);
// Assert
var controller = Assert.IsType<ControllerWithActivateAndFromServices>(result);
Assert.NotNull(controller.GetViewData());
}
[Fact]
public void CreateController_SetsBindingContext()
{
// Arrange
var actionDescriptor = new ControllerActionDescriptor
{
ControllerTypeInfo = typeof(ControllerWithActivateAndFromServices).GetTypeInfo()
};
var bindingContext = new ActionBindingContext();
var services = GetServices();
services.GetRequiredService<IScopedInstance<ActionBindingContext>>().Value = bindingContext;
var httpContext = new DefaultHttpContext
{
RequestServices = services
};
var context = new ActionContext(httpContext, new RouteData(), actionDescriptor);
var factory = new DefaultControllerFactory(new DefaultControllerActivator());
// Act
var result = factory.CreateController(context);
// Assert
var controller = Assert.IsType<ControllerWithActivateAndFromServices>(result);
Assert.Same(bindingContext, controller.BindingContext);
}
[Fact]
public void CreateController_PopulatesServicesFromServiceContainer()
{
// Arrange
var actionDescriptor = new ControllerActionDescriptor
{
ControllerTypeInfo = typeof(ControllerWithActivateAndFromServices).GetTypeInfo()
};
var services = GetServices();
var urlHelper = services.GetRequiredService<IUrlHelper>();
var httpContext = new DefaultHttpContext
{
RequestServices = services
};
var context = new ActionContext(httpContext, new RouteData(), actionDescriptor);
var factory = new DefaultControllerFactory(new DefaultControllerActivator());
// Act
var result = factory.CreateController(context);
// Assert
var controller = Assert.IsType<ControllerWithActivateAndFromServices>(result);
Assert.Same(urlHelper, controller.Helper);
}
[Fact]
public void CreateController_PopulatesUserServicesFromServiceContainer()
{
// Arrange
var actionDescriptor = new ControllerActionDescriptor
{
ControllerTypeInfo = typeof(ControllerWithActivateAndFromServices).GetTypeInfo()
};
var services = GetServices();
var testService = services.GetService<TestService>();
var httpContext = new DefaultHttpContext
{
RequestServices = services
};
var context = new ActionContext(httpContext, new RouteData(), actionDescriptor);
var factory = new DefaultControllerFactory(new DefaultControllerActivator());
// Act
var result = factory.CreateController(context);
// Assert
var controller = Assert.IsType<ControllerWithActivateAndFromServices>(result);
Assert.Same(testService, controller.TestService);
}
[Fact]
public void CreateController_IgnoresPropertiesThatAreNotDecoratedWithActivateAttribute()
{
// Arrange
var actionDescriptor = new ControllerActionDescriptor
{
ControllerTypeInfo = typeof(ControllerWithActivateAndFromServices).GetTypeInfo()
};
var services = GetServices();
var httpContext = new DefaultHttpContext
{
RequestServices = services
};
var context = new ActionContext(httpContext, new RouteData(), actionDescriptor);
var factory = new DefaultControllerFactory(new DefaultControllerActivator());
// Act
var result = factory.CreateController(context);
// Assert
var controller = Assert.IsType<ControllerWithActivateAndFromServices>(result);
Assert.Null(controller.Response);
}
[Fact]
public void CreateController_ThrowsIfPropertyCannotBeActivated()
{
// Arrange
var actionDescriptor = new ControllerActionDescriptor
{
ControllerTypeInfo = typeof(ControllerThatCannotBeActivated).GetTypeInfo()
};
var services = GetServices();
var httpContext = new DefaultHttpContext
{
RequestServices = services
};
var context = new ActionContext(httpContext, new RouteData(), actionDescriptor);
var factory = new DefaultControllerFactory(new DefaultControllerActivator());
// Act and Assert
var exception = Assert.Throws<InvalidOperationException>(() => factory.CreateController(context));
Assert.Equal("The property 'Service' on controller '" + typeof(ControllerThatCannotBeActivated) +
"' cannot be activated.", exception.Message);
}
[Fact]
public void DefaultControllerFactory_DisposesIDisposableController()
{
// Arrange
var factory = new DefaultControllerFactory(
Mock.Of<IServiceProvider>(),
Mock.Of<ITypeActivator>(),
Mock.Of<IControllerActivator>());
var factory = new DefaultControllerFactory(Mock.Of<IControllerActivator>());
var controller = new MyController();
// Act + Assert
@ -33,17 +259,64 @@ namespace Microsoft.AspNet.Mvc.Core.Test
public void DefaultControllerFactory_ReleasesNonIDisposableController()
{
// Arrange
var factory = new DefaultControllerFactory(
Mock.Of<IServiceProvider>(),
Mock.Of<ITypeActivator>(),
Mock.Of<IControllerActivator>());
var controller = new Object();
var factory = new DefaultControllerFactory(Mock.Of<IControllerActivator>());
var controller = new object();
// Act + Assert (does not throw)
factory.ReleaseController(controller);
}
private IServiceProvider GetServices()
{
var services = new Mock<IServiceProvider>();
services.Setup(s => s.GetService(typeof(IUrlHelper)))
.Returns(Mock.Of<IUrlHelper>());
services.Setup(s => s.GetService(typeof(IModelMetadataProvider)))
.Returns(new EmptyModelMetadataProvider());
services.Setup(s => s.GetService(typeof(TestService)))
.Returns(new TestService());
services
.Setup(s => s.GetService(typeof(IScopedInstance<ActionBindingContext>)))
.Returns(new MockScopedInstance<ActionBindingContext>());
return services.Object;
}
private class ControllerWithActivateAndFromServices
{
[Activate]
public ActionContext ActionContext { get; set; }
[Activate]
public ActionBindingContext BindingContext { get; set; }
[Activate]
public HttpContext HttpContext { get; set; }
[Activate]
protected HttpRequest Request { get; set; }
[Activate]
private ViewDataDictionary ViewData { get; set; }
[FromServices]
public IUrlHelper Helper { get; set; }
[FromServices]
public TestService TestService { get; set; }
public HttpResponse Response { get; set; }
public ViewDataDictionary GetViewData()
{
return ViewData;
}
public HttpRequest GetHttpRequest()
{
return Request;
}
}
private class MyController : Controller
{
public bool Disposed { get; set; }
@ -53,5 +326,16 @@ namespace Microsoft.AspNet.Mvc.Core.Test
Disposed = true;
}
}
private class ControllerThatCannotBeActivated
{
[Activate]
public TestService Service { get; set; }
}
private class TestService
{
}
}
}

View File

@ -0,0 +1,217 @@
// 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.Linq;
using System.Reflection;
using Microsoft.AspNet.Mvc.DefaultControllerTypeProviderControllers;
using Xunit;
namespace Microsoft.AspNet.Mvc
{
public class DefaultControllerTypeProviderTest
{
[Fact]
public void IsController_UserDefinedClass()
{
// Arrange
var typeInfo = typeof(StoreController).GetTypeInfo();
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
// Assert
Assert.True(isController);
}
[Fact]
public void IsController_FrameworkControllerClass()
{
// Arrange
var typeInfo = typeof(Controller).GetTypeInfo();
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
// Assert
Assert.False(isController);
}
[Fact]
public void IsController_UserDefinedControllerClass()
{
// Arrange
var typeInfo = typeof(DefaultControllerTypeProviderControllers.Controller).GetTypeInfo();
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
// Assert
Assert.False(isController);
}
[Fact]
public void IsController_Interface()
{
// Arrange
var typeInfo = typeof(IController).GetTypeInfo();
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
// Assert
Assert.False(isController);
}
[Fact]
public void IsController_AbstractClass()
{
// Arrange
var typeInfo = typeof(AbstractController).GetTypeInfo();
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
// Assert
Assert.False(isController);
}
[Fact]
public void IsController_DerivedAbstractClass()
{
// Arrange
var typeInfo = typeof(DerivedAbstractController).GetTypeInfo();
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
// Assert
Assert.True(isController);
}
[Fact]
public void IsController_OpenGenericClass()
{
// Arrange
var typeInfo = typeof(OpenGenericController<>).GetTypeInfo();
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
// Assert
Assert.False(isController);
}
[Fact]
public void IsController_ClosedGenericClass()
{
// Arrange
var typeInfo = typeof(OpenGenericController<string>).GetTypeInfo();
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
// Assert
Assert.True(isController);
}
[Fact]
public void IsController_DerivedGenericClass()
{
// Arrange
var typeInfo = typeof(DerivedGenericController).GetTypeInfo();
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
// Assert
Assert.True(isController);
}
[Fact]
public void IsController_Poco_WithNamingConvention()
{
// Arrange
var typeInfo = typeof(PocoController).GetTypeInfo();
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
// Assert
Assert.True(isController);
}
[Fact]
public void IsController_NoControllerSuffix()
{
// Arrange
var typeInfo = typeof(NoSuffix).GetTypeInfo();
var provider = GetControllerTypeProvider();
// Act
var isController = provider.IsController(typeInfo);
// Assert
Assert.True(isController);
}
private static DefaultControllerTypeProvider GetControllerTypeProvider()
{
var assemblyProvider = new FixedSetAssemblyProvider();
return new DefaultControllerTypeProvider(assemblyProvider, NullLoggerFactory.Instance);
}
}
}
// These controllers are used to test the DefaultControllerTypeProvider implementation
// which REQUIRES that they be public top-level classes. To avoid having to stub out the
// implementation of this class to test it, they are just top level classes. Don't reuse
// these outside this test - find a better way or use nested classes to keep the tests
// independent.
namespace Microsoft.AspNet.Mvc.DefaultControllerTypeProviderControllers
{
public abstract class AbstractController : Mvc.Controller
{
}
public class DerivedAbstractController : AbstractController
{
}
public class StoreController : Mvc.Controller
{
}
public class Controller
{
}
public class OpenGenericController<T> : Mvc.Controller
{
}
public class DerivedGenericController : OpenGenericController<string>
{
}
public interface IController
{
}
public class NoSuffix : Mvc.Controller
{
}
public class PocoController
{
}
}

View File

@ -0,0 +1,66 @@
// 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 Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Routing;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc
{
public class ServiceBasedControllerActivatorTest
{
[Fact]
public void Create_GetsServicesFromServiceProvider()
{
// Arrange
var controller = new DIController();
var serviceProvider = new Mock<IServiceProvider>(MockBehavior.Strict);
serviceProvider.Setup(s => s.GetService(typeof(DIController)))
.Returns(controller)
.Verifiable();
var httpContext = new DefaultHttpContext
{
RequestServices = serviceProvider.Object
};
var activator = new ServiceBasedControllerActivator();
var actionContext = new ActionContext(httpContext,
new RouteData(),
new ActionDescriptor());
// Act
var instance = activator.Create(actionContext, typeof(DIController));
// Assert
Assert.Same(controller, instance);
serviceProvider.Verify();
}
[Fact]
public void Create_ThrowsIfControllerIsNotRegisteredInServiceProvider()
{
// Arrange
var expected = "No service for type '" + typeof(DIController) + "' has been registered.";
var controller = new DIController();
var serviceProvider = new Mock<IServiceProvider>();
var httpContext = new DefaultHttpContext
{
RequestServices = serviceProvider.Object
};
var activator = new ServiceBasedControllerActivator();
var actionContext = new ActionContext(httpContext,
new RouteData(),
new ActionDescriptor());
// Act and Assert
var ex = Assert.Throws<InvalidOperationException>(
() => activator.Create(actionContext, typeof(DIController)));
Assert.Equal(expected, ex.Message);
}
private class DIController : Controller
{
}
}
}

View File

@ -1,22 +0,0 @@
// 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.Collections.Generic;
using System.Reflection;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// An implementation of IAssemblyProvider that provides just this assembly.
/// </summary>
public class StaticAssemblyProvider : IAssemblyProvider
{
public IEnumerable<Assembly> CandidateAssemblies
{
get
{
yield return typeof(StaticAssemblyProvider).GetTypeInfo().Assembly;
}
}
}
}

View File

@ -1,29 +0,0 @@
// 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.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Microsoft.AspNet.Mvc.ApplicationModels
{
/// <summary>
/// An implementation of StaticControllerModelBuilder that only allows controllers
/// from a fixed set of types.
/// </summary>
public class StaticControllerModelBuilder : DefaultControllerModelBuilder
{
public StaticControllerModelBuilder(params TypeInfo[] controllerTypes)
: base(new DefaultActionModelBuilder(), new NullLoggerFactory())
{
ControllerTypes = new List<TypeInfo>(controllerTypes ?? Enumerable.Empty<TypeInfo>());
}
public List<TypeInfo> ControllerTypes { get; private set; }
protected override bool IsController([NotNull] TypeInfo typeInfo)
{
return ControllerTypes.Contains(typeInfo);
}
}
}

View File

@ -177,7 +177,7 @@ namespace Microsoft.AspNet.Mvc
private class FilteredViewComponentSelector : DefaultViewComponentSelector
{
public FilteredViewComponentSelector()
: base(new StaticAssemblyProvider())
: base(GetAssemblyProvider())
{
AllowedTypes = typeof(DefaultViewComponentSelectorTest).GetNestedTypes(BindingFlags.NonPublic);
}
@ -188,6 +188,15 @@ namespace Microsoft.AspNet.Mvc
{
return AllowedTypes.Contains(typeInfo.AsType());
}
private static IAssemblyProvider GetAssemblyProvider()
{
var assemblyProvider = new FixedSetAssemblyProvider();
assemblyProvider.CandidateAssemblies.Add(
typeof(FilteredViewComponentSelector).GetTypeInfo().Assembly);
return assemblyProvider;
}
}
}
}

View File

@ -22,8 +22,8 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
var expectedMessage = "No service for type 'ActivatorWebSite.CannotBeActivatedController+FakeType' " +
"has been registered.";
var expectedMessage = "The property 'Service' on controller 'ActivatorWebSite.CannotBeActivatedController' " +
"cannot be activated.";
// Act & Assert
var response = await client.GetAsync("http://localhost/CannotBeActivated/Index");

View File

@ -0,0 +1,103 @@
// 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.Net;
using System.Net.Http;
using System.Threading.Tasks;
using ControllersFromServicesWebSite;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.TestHost;
using Xunit;
namespace Microsoft.AspNet.Mvc.FunctionalTests
{
public class ControllerFromServicesTest
{
private readonly IServiceProvider _provider = TestHelper.CreateServices(
nameof(ControllersFromServicesWebSite));
private readonly Action<IApplicationBuilder> _app = new Startup().Configure;
[Fact]
public async Task ControllersWithConstructorInjectionAreCreatedAndActivated()
{
// Arrange
var expected = "/constructorinjection 14 test-header-value";
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
client.DefaultRequestHeaders.TryAddWithoutValidation("Test-Header", "test-header-value");
// Act
var response = await client.GetStringAsync("http://localhost/constructorinjection?value=14");
// Assert
Assert.Equal(expected, response);
}
[Fact]
public async Task TypesDerivingFromControllerAreRegistered()
{
// Arrange
var expected = "No schedules available for 23";
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
// Act
var response = await client.GetStringAsync("http://localhost/schedule/23");
// Assert
Assert.Equal(expected, response);
}
[Fact]
public async Task TypesWithControllerSuffixAreRegistered()
{
// Arrange
var expected = "Updated record employee303";
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
// Act
var response = await client.PutAsync("http://localhost/employee/update_records?recordId=employee303",
new StringContent(string.Empty));
// Assert
response.EnsureSuccessStatusCode();
Assert.Equal(expected, await response.Content.ReadAsStringAsync());
}
[Fact]
public async Task TypesWithControllerSuffixAreConventionalRouted()
{
// Arrange
var expected = "Saved record employee #211";
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
// Act
var response = await client.PostAsync("http://localhost/employeerecords/save/211",
new StringContent(string.Empty));
// Assert
response.EnsureSuccessStatusCode();
Assert.Equal(expected, await response.Content.ReadAsStringAsync());
}
[Theory]
[InlineData("generic")]
[InlineData("nested")]
[InlineData("not-in-services")]
public async Task AddControllersFromServices_UsesControllerDiscoveryContentions(string action)
{
// Arrange
var server = TestServer.Create(_provider, _app);
var client = server.CreateClient();
// Act
var response = await client.GetAsync("http://localhost/not-discovered/" + action);
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
}
}

View File

@ -40,31 +40,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
}
}
[Fact]
public async Task IsControllerValues_LoggedAtStartup()
{
// Arrange and Act
var logs = await GetLogsByDataTypeAsync<IsControllerValues>();
// Assert
Assert.NotEmpty(logs);
foreach (var log in logs)
{
dynamic isController = log.State;
if (string.Equals(typeof(HomeController).AssemblyQualifiedName, isController.Type.ToString()))
{
Assert.Equal(
ControllerStatus.IsController,
Enum.Parse(typeof(ControllerStatus), isController.Status.ToString()));
}
else
{
Assert.NotEqual(ControllerStatus.IsController,
Enum.Parse(typeof(ControllerStatus), isController.Status.ToString()));
}
}
}
[Fact]
public async Task ControllerModelValues_LoggedAtStartup()
{

View File

@ -17,6 +17,7 @@
"BasicWebSite": "1.0.0",
"CompositeViewEngineWebSite": "1.0.0",
"ConnegWebSite": "1.0.0",
"ControllersFromServicesWebSite": "1.0.0",
"CustomRouteWebSite": "1.0.0",
"ErrorPageMiddlewareWebSite": "1.0.0",
"FilesWebSite": "1.0.0",

View File

@ -0,0 +1,153 @@
// 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.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNet.Mvc.MvcServiceCollectionExtensionsTestControllers;
using Microsoft.Framework.ConfigurationModel;
using Microsoft.Framework.DependencyInjection;
using Xunit;
namespace Microsoft.AspNet.Mvc
{
public class MvcServiceCollectionExtensionsTest
{
[Fact]
public void WithControllersAsServices_AddsTypesToControllerTypeProviderAndServiceCollection()
{
// Arrange
var collection = new ServiceCollection();
var controllerTypes = new[] { typeof(ControllerTypeA).GetTypeInfo(), typeof(TypeBController).GetTypeInfo() };
// Act
MvcServiceCollectionExtensions.WithControllersAsServices(collection,
controllerTypes);
// Assert
var services = collection.ToList();
Assert.Equal(4, services.Count);
Assert.Equal(typeof(ControllerTypeA), services[0].ServiceType);
Assert.Equal(typeof(ControllerTypeA), services[0].ImplementationType);
Assert.Equal(LifecycleKind.Transient, services[0].Lifecycle);
Assert.Equal(typeof(TypeBController), services[1].ServiceType);
Assert.Equal(typeof(TypeBController), services[1].ImplementationType);
Assert.Equal(LifecycleKind.Transient, services[1].Lifecycle);
Assert.Equal(typeof(IControllerActivator), services[2].ServiceType);
Assert.Equal(typeof(ServiceBasedControllerActivator), services[2].ImplementationType);
Assert.Equal(LifecycleKind.Transient, services[2].Lifecycle);
Assert.Equal(typeof(IControllerTypeProvider), services[3].ServiceType);
var typeProvider = Assert.IsType<FixedSetControllerTypeProvider>(services[3].ImplementationInstance);
Assert.Equal(controllerTypes, typeProvider.ControllerTypes.OrderBy(c => c.Name));
Assert.Equal(LifecycleKind.Singleton, services[3].Lifecycle);
}
[Fact]
public void WithControllersAsServices_UsesConfigurationIfSpecified()
{
// Arrange
var collection = new ServiceCollection();
var controllerTypes = new[] { typeof(ControllerTypeA), typeof(TypeBController) };
var configuration = new Configuration();
configuration.Add(new MemoryConfigurationSource());
configuration.Set(typeof(IControllerActivator).FullName,
typeof(CustomActivator).AssemblyQualifiedName);
configuration.Set(typeof(IControllerTypeProvider).FullName,
typeof(CustomTypeProvider).AssemblyQualifiedName);
// Act
MvcServiceCollectionExtensions.WithControllersAsServices(collection,
controllerTypes,
configuration);
// Assert
var services = collection.ToList();
Assert.Equal(4, services.Count);
Assert.Equal(typeof(ControllerTypeA), services[0].ServiceType);
Assert.Equal(typeof(ControllerTypeA), services[0].ImplementationType);
Assert.Equal(LifecycleKind.Transient, services[0].Lifecycle);
Assert.Equal(typeof(TypeBController), services[1].ServiceType);
Assert.Equal(typeof(TypeBController), services[1].ImplementationType);
Assert.Equal(LifecycleKind.Transient, services[1].Lifecycle);
Assert.Equal(typeof(IControllerActivator), services[2].ServiceType);
Assert.Equal(typeof(CustomActivator), services[2].ImplementationType);
Assert.Equal(LifecycleKind.Transient, services[2].Lifecycle);
Assert.Equal(typeof(IControllerTypeProvider), services[3].ServiceType);
Assert.Equal(typeof(CustomTypeProvider), services[3].ImplementationType);
Assert.Equal(LifecycleKind.Singleton, services[3].Lifecycle);
}
[Fact]
public void WithControllersAsServices_ScansControllersFromSpecifiedAssemblies()
{
// Arrange
var collection = new ServiceCollection();
var assemblies = new[] { GetType().Assembly };
var configuration = new Configuration();
var controllerTypes = new[] { typeof(ControllerTypeA), typeof(TypeBController) };
// Act
MvcServiceCollectionExtensions.WithControllersAsServices(collection,
assemblies);
// Assert
var services = collection.ToList();
Assert.Equal(4, services.Count);
Assert.Equal(typeof(ControllerTypeA), services[0].ServiceType);
Assert.Equal(typeof(ControllerTypeA), services[0].ImplementationType);
Assert.Equal(LifecycleKind.Transient, services[0].Lifecycle);
Assert.Equal(typeof(TypeBController), services[1].ServiceType);
Assert.Equal(typeof(TypeBController), services[1].ImplementationType);
Assert.Equal(LifecycleKind.Transient, services[1].Lifecycle);
Assert.Equal(typeof(IControllerActivator), services[2].ServiceType);
Assert.Equal(typeof(ServiceBasedControllerActivator), services[2].ImplementationType);
Assert.Equal(LifecycleKind.Transient, services[2].Lifecycle);
Assert.Equal(typeof(IControllerTypeProvider), services[3].ServiceType);
var typeProvider = Assert.IsType<FixedSetControllerTypeProvider>(services[3].ImplementationInstance);
Assert.Equal(controllerTypes, typeProvider.ControllerTypes.OrderBy(c => c.Name));
Assert.Equal(LifecycleKind.Singleton, services[3].Lifecycle);
}
private class CustomActivator : IControllerActivator
{
public object Create(ActionContext context, Type controllerType)
{
throw new NotImplementedException();
}
}
public class CustomTypeProvider : IControllerTypeProvider
{
public IEnumerable<TypeInfo> ControllerTypes { get; set; }
}
}
}
// These controllers are used to test the UseControllersAsServices implementation
// which REQUIRES that they be public top-level classes. To avoid having to stub out the
// implementation of this class to test it, they are just top level classes. Don't reuse
// these outside this test - find a better way or use nested classes to keep the tests
// independent.
namespace Microsoft.AspNet.Mvc.MvcServiceCollectionExtensionsTestControllers
{
public class ControllerTypeA : Controller
{
}
public class TypeBController
{
}
}

View File

@ -8,6 +8,7 @@ using System.Linq;
using System.Reflection;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.ApplicationModels;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.Filters;
using Microsoft.AspNet.Mvc.WebApiCompatShim;
using Microsoft.Framework.DependencyInjection;
@ -368,10 +369,11 @@ namespace System.Web.Http
private INestedProviderManager<ActionDescriptorProviderContext> CreateProvider()
{
var assemblyProvider = new Mock<IAssemblyProvider>();
assemblyProvider
.SetupGet(ap => ap.CandidateAssemblies)
.Returns(new Assembly[] { typeof(ApiControllerActionDiscoveryTest).Assembly });
var assemblyProvider = new FixedSetAssemblyProvider();
assemblyProvider.CandidateAssemblies.Add(GetType().GetTypeInfo().Assembly);
var controllerTypeProvider = new NamespaceFilteredControllerTypeProvider(assemblyProvider);
var modelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
var filterProvider = new Mock<IGlobalFilterProvider>();
filterProvider
@ -389,8 +391,8 @@ namespace System.Web.Http
.Returns(options);
var provider = new ControllerActionDescriptorProvider(
assemblyProvider.Object,
new NamespaceLimitedActionDiscoveryConventions(),
controllerTypeProvider,
modelBuilder,
filterProvider.Object,
optionsAccessor.Object,
new NullLoggerFactory());
@ -402,18 +404,21 @@ namespace System.Web.Http
});
}
private class NamespaceLimitedActionDiscoveryConventions : DefaultControllerModelBuilder
private class NamespaceFilteredControllerTypeProvider : DefaultControllerTypeProvider
{
public NamespaceLimitedActionDiscoveryConventions()
: base(new DefaultActionModelBuilder(), new NullLoggerFactory())
public NamespaceFilteredControllerTypeProvider(IAssemblyProvider provider)
: base(provider, NullLoggerFactory.Instance)
{
}
protected override bool IsController(TypeInfo typeInfo)
public override IEnumerable<TypeInfo> ControllerTypes
{
return
typeInfo.Namespace == "System.Web.Http.TestControllers" &&
base.IsController(typeInfo);
get
{
return base.ControllerTypes
.Where(typeInfo => typeInfo.Namespace == "System.Web.Http.TestControllers");
}
}
}
}

View File

@ -8,7 +8,7 @@ namespace ActivatorWebSite
{
public class PlainController
{
[Activate]
[FromServices]
public MyService Service { get; set; }
[Activate]

View File

@ -0,0 +1,36 @@
// 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 Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc;
namespace ControllersFromServicesClassLibrary
{
public class ConstructorInjectionController
{
public ConstructorInjectionController(IUrlHelper urlHelper,
QueryValueService queryService)
{
UrlHelper = urlHelper;
QueryService = queryService;
}
private IUrlHelper UrlHelper { get; }
private QueryValueService QueryService { get; }
[Activate]
public HttpRequest Request { get; set; }
[HttpGet("/constructorinjection")]
public IActionResult Index()
{
var content = string.Join(" ",
UrlHelper.Action(),
QueryService.GetValue(),
Request.Headers["Test-Header"]);
return new ContentResult { Content = content };
}
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="__ToolsVersion__" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>551dc89e-2a13-4cf2-83d7-1add802443d5</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,24 @@
// 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 Microsoft.AspNet.Mvc;
namespace ControllersFromServicesClassLibrary
{
public class EmployeeRecords : Controller
{
[HttpPut("/employee/update_records")]
public IActionResult UpdateRecords(string recordId)
{
return Content("Updated record " + recordId);
}
[HttpPost]
// This action uses conventional routing.
public IActionResult Save(string id)
{
return Content("Saved record employee #" + id);
}
}
}

View File

@ -0,0 +1,16 @@
// 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 Microsoft.AspNet.Mvc;
namespace ControllersFromServicesClassLibrary
{
public class GenericController<TController> : Controller
{
[HttpGet("/not-discovered/generic")]
public IActionResult Index()
{
return new EmptyResult();
}
}
}

View File

@ -0,0 +1,19 @@
// 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 Microsoft.AspNet.Mvc;
namespace ControllersFromServicesClassLibrary
{
public class NestedControllerOwner
{
public class NestedController : Controller
{
[HttpGet("/not-discovered/nested")]
public IActionResult Index()
{
return new EmptyResult();
}
}
}
}

View File

@ -0,0 +1,23 @@
// 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 Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
namespace ControllersFromServicesClassLibrary
{
public class QueryValueService
{
private readonly HttpContext _context;
public QueryValueService(IHttpContextAccessor httpContextAccessor)
{
_context = httpContextAccessor.Value;
}
public string GetValue()
{
return _context.Request.Query["value"];
}
}
}

View File

@ -0,0 +1,16 @@
// 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 Microsoft.AspNet.Mvc;
namespace ControllersFromServicesClassLibrary
{
public class TimeScheduleController
{
[HttpGet("/schedule/{id:int}")]
public IActionResult GetSchedule(int id)
{
return new ContentResult { Content = "No schedules available for " + id };
}
}
}

View File

@ -0,0 +1,9 @@
{
"dependencies": {
"Microsoft.AspNet.Mvc": "6.0.0-*",
},
"frameworks": {
"aspnet50": { },
"aspnetcore50": { }
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="__ToolsVersion__" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>983741b2-4424-4ed1-9b03-7675a67230c8</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<DevelopmentServerPort>42994</DevelopmentServerPort>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,16 @@
// 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 Microsoft.AspNet.Mvc;
namespace ControllersFromServicesWebSite
{
public class NotInServicesController : Controller
{
[HttpGet("/not-discovered/not-in-services")]
public IActionResult Index()
{
return View();
}
}
}

View File

@ -0,0 +1,54 @@
// 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.Reflection;
using Microsoft.AspNet.Builder;
using Microsoft.Framework.DependencyInjection;
using ControllersFromServicesClassLibrary;
#if ASPNET50
using Autofac;
using Microsoft.Framework.DependencyInjection.Autofac;
#endif
namespace ControllersFromServicesWebSite
{
public class Startup
{
public void Configure(IApplicationBuilder app)
{
var configuration = app.GetTestConfiguration();
app.UseServices(services =>
{
services.AddMvc(configuration)
.WithControllersAsServices(
new[]
{
typeof(TimeScheduleController).GetTypeInfo().Assembly
});
services.AddTransient<QueryValueService>();
#if ASPNET50
// Create the autofac container
var builder = new ContainerBuilder();
// Create the container and use the default application services as a fallback
AutofacRegistration.Populate(
builder,
services);
return builder.Build()
.Resolve<IServiceProvider>();
#endif
});
app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller}/{action}/{id}");
});
}
}
}

View File

@ -0,0 +1 @@
<view-data>@ViewBag.Value</view-data>

View File

@ -0,0 +1,24 @@
{
"commands": {
"web": "Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:5001",
"kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5000"
},
"dependencies": {
"ControllersFromServicesClassLibrary": "1.0.0",
"Kestrel": "1.0.0-*",
"Microsoft.AspNet.Mvc": "6.0.0-*",
"Microsoft.AspNet.Mvc.TestConfiguration": "1.0.0",
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
"Microsoft.AspNet.StaticFiles": "1.0.0-*"
},
"frameworks": {
"aspnet50": {
"dependencies": {
"Microsoft.Framework.DependencyInjection.Autofac": "1.0.0-*"
}
},
"aspnetcore50": { }
},
"webroot": "wwwroot"
}

View File

@ -0,0 +1 @@
Functional test site for verifying that controllers registered as services can be consumed by DefaultControllerFactory.

View File

@ -14,7 +14,7 @@ namespace ModelBindingWebSite.Controllers
private IHtmlHelper<Person> _personHelper;
private bool _activated;
[Activate]
[FromServices]
public IHtmlHelper<Person> PersonHelper
{
get

View File

@ -8,7 +8,7 @@ namespace MvcTagHelpersWebSite.Controllers
{
public class Catalog_CacheTagHelperController : Controller
{
[Activate]
[FromServices]
public ProductsService ProductsService { get; set; }
[HttpGet("/catalog")]

View File

@ -8,7 +8,7 @@ namespace RequestServicesWebSite
[Route("RequestScoped/[action]")]
public class RequestScopedServiceController
{
[Activate]
[FromServices]
public RequestIdService RequestIdService { get; set; }
[HttpGet]

View File

@ -15,7 +15,7 @@ namespace WebApiCompatShimWebSite
{
public class BasicApiController : ApiController
{
[Activate]
[FromServices]
public IOptions<WebApiCompatShimOptions> OptionsAccessor { get; set; }
// Verifies property activation