diff --git a/Mvc.sln b/Mvc.sln
index 87891aba29..45da5d60ac 100644
--- a/Mvc.sln
+++ b/Mvc.sln
@@ -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
diff --git a/samples/MvcSample.Web/HomeController.cs b/samples/MvcSample.Web/HomeController.cs
index 9389183c64..829b8f0b81 100644
--- a/samples/MvcSample.Web/HomeController.cs
+++ b/samples/MvcSample.Web/HomeController.cs
@@ -92,7 +92,7 @@ namespace MvcSample.Web
return View("MyView", user);
}
- [Activate]
+ [FromServices]
public IHostingEnvironment HostingEnvironment { get; set; }
///
diff --git a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/DefaultControllerModelBuilder.cs b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/DefaultControllerModelBuilder.cs
index e06c9e1e69..5c44f1240b 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/DefaultControllerModelBuilder.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/DefaultControllerModelBuilder.cs
@@ -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
///
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;
}
- ///
- /// Returns true if the is a controller. Otherwise false.
- ///
- /// The .
- /// true if the is a controller. Otherwise false.
- ///
- /// Override this method to provide custom logic to determine which types are considered controllers.
- ///
- 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;
- }
-
///
/// Creates an for the given .
///
diff --git a/src/Microsoft.AspNet.Mvc.Core/Controller.cs b/src/Microsoft.AspNet.Mvc.Core/Controller.cs
index 4350b2fb62..feb1f22050 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Controller.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Controller.cs
@@ -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
diff --git a/src/Microsoft.AspNet.Mvc.Core/ControllerActionDescriptorProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ControllerActionDescriptorProvider.cs
index 1e6b6589cf..75aade5bf6 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ControllerActionDescriptorProvider.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ControllerActionDescriptorProvider.cs
@@ -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 _globalFilters;
private readonly IEnumerable _conventions;
private readonly ILogger _logger;
- public ControllerActionDescriptorProvider([NotNull] IAssemblyProvider assemblyProvider,
+ public ControllerActionDescriptorProvider([NotNull] IControllerTypeProvider controllerTypeProvider,
[NotNull] IControllerModelBuilder applicationModelBuilder,
[NotNull] IGlobalFilterProvider globalFilters,
[NotNull] IOptions 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)
diff --git a/src/Microsoft.AspNet.Mvc.Core/DefaultAssemblyProvider.cs b/src/Microsoft.AspNet.Mvc.Core/DefaultAssemblyProvider.cs
index aa7f43835e..f54ed43e4d 100644
--- a/src/Microsoft.AspNet.Mvc.Core/DefaultAssemblyProvider.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/DefaultAssemblyProvider.cs
@@ -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);
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/DefaultControllerActivator.cs b/src/Microsoft.AspNet.Mvc.Core/DefaultControllerActivator.cs
index 69c95be847..615d614ef4 100644
--- a/src/Microsoft.AspNet.Mvc.Core/DefaultControllerActivator.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/DefaultControllerActivator.cs
@@ -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
{
///
- /// Represents the that is registered by default.
+ /// that uses type activation to create controllers.
///
public class DefaultControllerActivator : IControllerActivator
{
- private readonly Func[]> _getPropertiesToActivate;
- private readonly IDictionary> _valueAccessorLookup;
- private readonly ConcurrentDictionary[]> _injectActions;
+ private static readonly Func _createControllerFactory =
+ type => ActivatorUtilities.CreateFactory(type, Type.EmptyTypes);
- ///
- /// Initializes a new instance of the DefaultControllerActivator class.
- ///
- public DefaultControllerActivator()
+ private readonly ConcurrentDictionary _controllerFactories =
+ new ConcurrentDictionary();
+
+ ///
+ public object Create([NotNull] ActionContext actionContext, [NotNull] Type controllerType)
{
- _valueAccessorLookup = CreateValueAccessorLookup();
- _injectActions = new ConcurrentDictionary[]>();
- _getPropertiesToActivate = type =>
- PropertyActivator.GetPropertiesToActivate(type,
- typeof(ActivateAttribute),
- CreateActivateInfo);
- }
-
- ///
- /// Activates the specified controller by using the specified action context.
- ///
- /// The controller to activate.
- /// The context of the executing action.
- 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> CreateValueAccessorLookup()
- {
- var dictionary = new Dictionary>
- {
- { typeof(ActionContext), (context) => context },
- { typeof(HttpContext), (context) => context.HttpContext },
- { typeof(HttpRequest), (context) => context.HttpContext.Request },
- { typeof(HttpResponse), (context) => context.HttpContext.Response },
- {
- typeof(ViewDataDictionary),
- (context) =>
- {
- var serviceProvider = context.HttpContext.RequestServices;
- return new ViewDataDictionary(
- serviceProvider.GetRequiredService(),
- context.ModelState);
- }
- },
- {
- typeof(ActionBindingContext),
- (context) =>
- {
- var serviceProvider = context.HttpContext.RequestServices;
- var accessor = serviceProvider.GetRequiredService>();
- return accessor.Value;
- }
- }
- };
- return dictionary;
- }
-
- private PropertyActivator CreateActivateInfo(
- PropertyInfo property)
- {
- Func valueAccessor;
- if (!_valueAccessorLookup.TryGetValue(property.PropertyType, out valueAccessor))
- {
- valueAccessor = (actionContext) =>
- {
- var serviceProvider = actionContext.HttpContext.RequestServices;
- return serviceProvider.GetRequiredService(property.PropertyType);
- };
- }
-
- return new PropertyActivator(property, valueAccessor);
+ var factory = _controllerFactories.GetOrAdd(controllerType, _createControllerFactory);
+ return factory(actionContext.HttpContext.RequestServices, arguments: null);
}
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/DefaultControllerFactory.cs b/src/Microsoft.AspNet.Mvc.Core/DefaultControllerFactory.cs
index 8f88a1594e..c39ee7b168 100644
--- a/src/Microsoft.AspNet.Mvc.Core/DefaultControllerFactory.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/DefaultControllerFactory.cs
@@ -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
{
+ ///
+ /// Default implementation for .
+ ///
public class DefaultControllerFactory : IControllerFactory
{
- private readonly IServiceProvider _serviceProvider;
- private readonly ITypeActivator _typeActivator;
private readonly IControllerActivator _controllerActivator;
+ private readonly IDictionary> _valueAccessorLookup;
+ private readonly ConcurrentDictionary[]> _activateActions;
+ private readonly Func[]> _getPropertiesToActivate;
+ private readonly Func> _getRequiredService = GetRequiredService;
- public DefaultControllerFactory(IServiceProvider serviceProvider,
- ITypeActivator typeActivator,
- IControllerActivator controllerActivator)
+ ///
+ /// Initializes a new instance of .
+ ///
+ /// used to create controller
+ /// instances.
+ public DefaultControllerFactory(IControllerActivator controllerActivator)
{
- _serviceProvider = serviceProvider;
- _typeActivator = typeActivator;
_controllerActivator = controllerActivator;
+ _valueAccessorLookup = CreateValueAccessorLookup();
+ _activateActions = new ConcurrentDictionary[]>();
+ _getPropertiesToActivate = GetPropertiesToActivate;
}
- public object CreateController(ActionContext actionContext)
+ ///
+ 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;
}
+ ///
public void ReleaseController(object controller)
{
var disposableController = controller as IDisposable;
@@ -51,5 +66,111 @@ namespace Microsoft.AspNet.Mvc
disposableController.Dispose();
}
}
+
+ ///
+ /// Activates the specified controller using the specified action context.
+ ///
+ /// The controller to activate.
+ /// The context of the executing action.
+ protected virtual void ActivateProperties([NotNull] object controller, [NotNull] ActionContext context)
+ {
+ var controllerType = controller.GetType();
+ var 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);
+ }
+ }
+
+ ///
+ /// Returns a of property types to delegates used to activate
+ /// controller properties annotated with .
+ ///
+ /// A dictionary containing the property type to activator delegate mapping.
+ /// Override this method to provide custom activation behavior for controller properties
+ /// annotated with .
+ protected virtual IDictionary> CreateValueAccessorLookup()
+ {
+ var dictionary = new Dictionary>
+ {
+ { typeof(ActionContext), (context) => context },
+ { typeof(HttpContext), (context) => context.HttpContext },
+ { typeof(HttpRequest), (context) => context.HttpContext.Request },
+ { typeof(HttpResponse), (context) => context.HttpContext.Response },
+ {
+ typeof(ViewDataDictionary),
+ (context) =>
+ {
+ var serviceProvider = context.HttpContext.RequestServices;
+ return new ViewDataDictionary(
+ serviceProvider.GetRequiredService(),
+ context.ModelState);
+ }
+ },
+ {
+ typeof(ActionBindingContext),
+ (context) =>
+ {
+ var serviceProvider = context.HttpContext.RequestServices;
+ var accessor = serviceProvider.GetRequiredService>();
+ return accessor.Value;
+ }
+ }
+ };
+
+ return dictionary;
+ }
+
+ private PropertyActivator[] GetPropertiesToActivate(Type type)
+ {
+ var activatorsForActivateProperties = PropertyActivator.GetPropertiesToActivate(
+ type,
+ typeof(ActivateAttribute),
+ CreateActivateInfo);
+ var activatorsForFromServiceProperties = PropertyActivator.GetPropertiesToActivate(
+ type,
+ typeof(FromServicesAttribute),
+ CreateFromServicesInfo);
+
+ return Enumerable.Concat(activatorsForActivateProperties, activatorsForFromServiceProperties)
+ .ToArray();
+ }
+
+ private PropertyActivator CreateActivateInfo(
+ PropertyInfo property)
+ {
+ Func valueAccessor;
+ if (!_valueAccessorLookup.TryGetValue(property.PropertyType, out valueAccessor))
+ {
+ var message = Resources.FormatControllerFactory_PropertyCannotBeActivated(
+ property.Name,
+ property.DeclaringType.FullName);
+ throw new InvalidOperationException(message);
+ }
+
+ return new PropertyActivator(property, valueAccessor);
+ }
+
+ private PropertyActivator CreateFromServicesInfo(
+ PropertyInfo property)
+ {
+ var valueAccessor = _getRequiredService(property.PropertyType);
+ return new PropertyActivator(property, valueAccessor);
+ }
+
+ private static Func GetRequiredService(Type propertyType)
+ {
+ return actionContext => actionContext.HttpContext.RequestServices.GetRequiredService(propertyType);
+ }
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/DefaultControllerTypeProvider.cs b/src/Microsoft.AspNet.Mvc.Core/DefaultControllerTypeProvider.cs
new file mode 100644
index 0000000000..4e4ae48742
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/DefaultControllerTypeProvider.cs
@@ -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
+{
+ ///
+ /// A that identifies controller types from assemblies
+ /// specified by the registered .
+ ///
+ public class DefaultControllerTypeProvider : IControllerTypeProvider
+ {
+ private readonly IAssemblyProvider _assemblyProvider;
+ private readonly ILogger _logger;
+
+ ///
+ /// Initializes a new instance of .
+ ///
+ /// that provides assemblies to look for
+ /// controllers in.
+ /// The .
+ public DefaultControllerTypeProvider(IAssemblyProvider assemblyProvider,
+ ILoggerFactory loggerFactory)
+ {
+ _assemblyProvider = assemblyProvider;
+ _logger = loggerFactory.Create();
+ }
+
+ ///
+ public virtual IEnumerable 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);
+ }
+ }
+
+ ///
+ /// Returns true if the is a controller. Otherwise false.
+ ///
+ /// The .
+ /// true if the is a controller. Otherwise false.
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Core/FixedSetAssemblyProvider.cs b/src/Microsoft.AspNet.Mvc.Core/FixedSetAssemblyProvider.cs
new file mode 100644
index 0000000000..f5c5364026
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/FixedSetAssemblyProvider.cs
@@ -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
+{
+ ///
+ /// A with a fixed set of candidate assemblies.
+ ///
+ public class FixedSetAssemblyProvider : IAssemblyProvider
+ {
+ ///
+ /// Gets the list of candidate assemblies.
+ ///
+ public IList CandidateAssemblies { get; } = new List();
+
+ IEnumerable IAssemblyProvider.CandidateAssemblies => CandidateAssemblies;
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.Core/FixedSetControllerTypeProvider.cs b/src/Microsoft.AspNet.Mvc.Core/FixedSetControllerTypeProvider.cs
new file mode 100644
index 0000000000..b95f43fdc0
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/FixedSetControllerTypeProvider.cs
@@ -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
+{
+ ///
+ /// A with a fixed set of types that are used as controllers.
+ ///
+ public class FixedSetControllerTypeProvider : IControllerTypeProvider
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ public FixedSetControllerTypeProvider()
+ : this(Enumerable.Empty())
+ {
+ }
+
+ ///
+ /// Initializes a new instance of .
+ ///
+ /// The sequence of controller .
+ public FixedSetControllerTypeProvider([NotNull] IEnumerable controllerTypes)
+ {
+ ControllerTypes = new List(controllerTypes);
+ }
+
+ ///
+ /// Gets the list of controller s.
+ ///
+ public IList ControllerTypes { get; }
+
+ IEnumerable IControllerTypeProvider.ControllerTypes => ControllerTypes;
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.Core/IAssemblyProvider.cs b/src/Microsoft.AspNet.Mvc.Core/IAssemblyProvider.cs
index a5a9cdfbb3..8a6fc23710 100644
--- a/src/Microsoft.AspNet.Mvc.Core/IAssemblyProvider.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/IAssemblyProvider.cs
@@ -6,8 +6,16 @@ using System.Reflection;
namespace Microsoft.AspNet.Mvc
{
+ ///
+ /// Specifies the contract for discovering assemblies that may contain Mvc specific types such as controllers,
+ /// view components and precompiled views.
+ ///
public interface IAssemblyProvider
{
+ ///
+ /// Gets the sequence of candidate ies that the application
+ /// uses for discovery of Mvc specific types.
+ ///
IEnumerable CandidateAssemblies { get; }
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/IControllerActivator.cs b/src/Microsoft.AspNet.Mvc.Core/IControllerActivator.cs
index e1f837f636..4c7ec1d9c4 100644
--- a/src/Microsoft.AspNet.Mvc.Core/IControllerActivator.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/IControllerActivator.cs
@@ -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
{
///
- /// Provides methods to activate an instantiated controller.
+ /// Provides methods to create a controller.
///
public interface IControllerActivator
{
///
- /// When implemented in a type, activates an instantiated controller.
+ /// Creates a controller.
///
- /// The controller to activate.
/// The for the executing action.
- void Activate(object controller, ActionContext context);
+ object Create(ActionContext context, Type controllerType);
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Core/IControllerFactory.cs b/src/Microsoft.AspNet.Mvc.Core/IControllerFactory.cs
index b58197ee92..de6f262d13 100644
--- a/src/Microsoft.AspNet.Mvc.Core/IControllerFactory.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/IControllerFactory.cs
@@ -3,9 +3,22 @@
namespace Microsoft.AspNet.Mvc
{
+ ///
+ /// Provides methods for creation and disposal of controllers.
+ ///
public interface IControllerFactory
{
+ ///
+ /// Creates a new controller for the specified .
+ ///
+ /// for the action to execute.
+ /// The controller.
object CreateController(ActionContext actionContext);
+
+ ///
+ /// Releases a controller instance.
+ ///
+ /// The controller.
void ReleaseController(object controller);
}
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/IControllerTypeProvider.cs b/src/Microsoft.AspNet.Mvc.Core/IControllerTypeProvider.cs
new file mode 100644
index 0000000000..b47259f24a
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/IControllerTypeProvider.cs
@@ -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
+{
+ ///
+ /// Provides methods for discovery of controller types.
+ ///
+ public interface IControllerTypeProvider
+ {
+ ///
+ /// Gets a of controller s.
+ ///
+ IEnumerable ControllerTypes { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/ControllerStatus.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/ControllerStatus.cs
deleted file mode 100644
index d64b8f8483..0000000000
--- a/src/Microsoft.AspNet.Mvc.Core/Logging/ControllerStatus.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Indicates the status of a class during controller discovery.
- /// All values except 0 represent a reason why a type is not a controller.
- ///
- [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
- }
-}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Core/Logging/IsControllerValues.cs b/src/Microsoft.AspNet.Mvc.Core/Logging/IsControllerValues.cs
deleted file mode 100644
index 3d6ddb8b9e..0000000000
--- a/src/Microsoft.AspNet.Mvc.Core/Logging/IsControllerValues.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Logged to indicate the state of a class during controller discovery. Logs the type
- /// of the controller as well as the .
- ///
- public class IsControllerValues : LoggerStructureBase
- {
- public IsControllerValues(Type type, ControllerStatus status)
- {
- Type = type;
- Status = status;
- }
-
- ///
- /// The of the potential class.
- ///
- public Type Type { get; }
-
- ///
- /// The of the .
- ///
- public ControllerStatus Status { get; }
-
- public override string Format()
- {
- return LogFormatter.FormatStructure(this);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs
index 2199f4decc..4954301d8f 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs
@@ -1642,6 +1642,22 @@ namespace Microsoft.AspNet.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("Format_NotValid"), p0);
}
+ ///
+ /// The property '{0}' on controller '{1}' cannot be activated.
+ ///
+ internal static string ControllerFactory_PropertyCannotBeActivated
+ {
+ get { return GetString("ControllerFactory_PropertyCannotBeActivated"); }
+ }
+
+ ///
+ /// The property '{0}' on controller '{1}' cannot be activated.
+ ///
+ internal static string FormatControllerFactory_PropertyCannotBeActivated(object p0, object p1)
+ {
+ return string.Format(CultureInfo.CurrentCulture, GetString("ControllerFactory_PropertyCannotBeActivated"), p0, p1);
+ }
+
///
/// No URL for remote validation could be found.
///
diff --git a/src/Microsoft.AspNet.Mvc.Core/Resources.resx b/src/Microsoft.AspNet.Mvc.Core/Resources.resx
index 78a8b4faf5..c5434b5443 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Resources.resx
+++ b/src/Microsoft.AspNet.Mvc.Core/Resources.resx
@@ -433,6 +433,9 @@
The format provided is invalid '{0}'. A format must be a non-empty file-extension, optionally prefixed with a '.' character.
+
+ The property '{0}' on controller '{1}' cannot be activated.
+
No URL for remote validation could be found.
diff --git a/src/Microsoft.AspNet.Mvc.Core/ServiceBasedControllerActivator.cs b/src/Microsoft.AspNet.Mvc.Core/ServiceBasedControllerActivator.cs
new file mode 100644
index 0000000000..87ac9879c6
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/ServiceBasedControllerActivator.cs
@@ -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
+{
+ ///
+ /// A that retrieves controllers as services from the request's
+ /// .
+ ///
+ public class ServiceBasedControllerActivator : IControllerActivator
+ {
+ ///
+ public object Create([NotNull] ActionContext actionContext, [NotNull] Type controllerType)
+ {
+ return actionContext.HttpContext.RequestServices.GetRequiredService(controllerType);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs
index ac9db2ea00..8397ac997e 100644
--- a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs
+++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs
@@ -51,7 +51,7 @@ namespace System.Web.Http
/// Gets the .
///
/// The setter is intended for unit testing purposes only.
- [Activate]
+ [FromServices]
public IModelMetadataProvider MetadataProvider { get; set; }
///
@@ -91,7 +91,7 @@ namespace System.Web.Http
/// Gets a factory used to generate URLs to other APIs.
///
/// The setter is intended for unit testing purposes only.
- [Activate]
+ [FromServices]
public IUrlHelper Url { get; set; }
///
diff --git a/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs b/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs
index 3f50f69fe0..0b586212e7 100644
--- a/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs
+++ b/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs
@@ -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);
}
+ ///
+ /// Register the specified as services and as a source for controller
+ /// discovery.
+ ///
+ /// The .
+ /// A sequence of controller s to register in the
+ /// and used for controller discovery.
+ /// The .
+ public static IServiceCollection WithControllersAsServices(
+ [NotNull] this IServiceCollection services,
+ [NotNull] IEnumerable controllerTypes)
+ {
+ return WithControllersAsServices(services, controllerTypes, configuration: null);
+ }
+
+ ///
+ /// Register the specified as services and as a source for controller
+ /// discovery.
+ ///
+ /// The .
+ /// A sequence of controller s to register
+ /// in the and used for controller discovery.
+ /// The application's .
+ /// The .
+ public static IServiceCollection WithControllersAsServices(
+ [NotNull] this IServiceCollection services,
+ [NotNull] IEnumerable 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());
+ services.Replace(describer.Instance(controllerTypeProvider));
+
+ return services;
+ }
+
+ ///
+ /// Registers controller types from the specified as services and as a source
+ /// for controller discovery.
+ ///
+ /// The .
+ /// Assemblies to scan.
+ /// The .
+ public static IServiceCollection WithControllersAsServices(
+ [NotNull] this IServiceCollection services,
+ [NotNull] IEnumerable controllerAssemblies)
+ {
+ return WithControllersAsServices(services,
+ controllerAssemblies,
+ configuration: null);
+ }
+
+ ///
+ /// Registers controller types from the specified as services and as a source
+ /// for controller discovery.
+ ///
+ /// The .
+ /// Assemblies to scan.
+ /// The application's .
+ /// The .
+ public static IServiceCollection WithControllersAsServices(
+ [NotNull] this IServiceCollection services,
+ [NotNull] IEnumerable 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);
diff --git a/src/Microsoft.AspNet.Mvc/MvcServices.cs b/src/Microsoft.AspNet.Mvc/MvcServices.cs
index 43921f5e99..62f1f5d090 100644
--- a/src/Microsoft.AspNet.Mvc/MvcServices.cs
+++ b/src/Microsoft.AspNet.Mvc/MvcServices.cs
@@ -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();
yield return describe.Transient();
yield return describe.Transient();
- // This accesses per-request services to activate the controller
- yield return describe.Transient();
+ // This has a cache, so it needs to be a singleton
+ yield return describe.Singleton();
// This has a cache, so it needs to be a singleton
yield return describe.Singleton();
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/DefaultControllerModelBuilderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/DefaultControllerModelBuilderTest.cs
index cb37c4aea9..7505e2e021 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/DefaultControllerModelBuilderTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/DefaultControllerModelBuilderTest.cs
@@ -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).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 : Mvc.Controller
- {
- }
-
- public class DerivedGenericController : OpenGenericController
- {
- }
-
- 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();
- }
- }
-}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionDescriptorProviderTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionDescriptorProviderTests.cs
index 9459fae59d..859eb8123e 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionDescriptorProviderTests.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionDescriptorProviderTests.cs
@@ -1362,16 +1362,13 @@ namespace Microsoft.AspNet.Mvc.Test
TypeInfo controllerTypeInfo,
IEnumerable filters = null)
{
- var modelBuilder = new StaticControllerModelBuilder(controllerTypeInfo);
-
- var assemblyProvider = new Mock();
- 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();
- 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();
- 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 GetDescriptors(params TypeInfo[] controllerTypeInfos)
{
- var modelBuilder = new StaticControllerModelBuilder(controllerTypeInfos);
-
- var assemblyProvider = new Mock();
- 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(),
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionDescriptorCollectionProviderLoggingTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionDescriptorCollectionProviderLoggingTest.cs
index 5e8761f81c..5efdd49675 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionDescriptorCollectionProviderLoggingTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionDescriptorCollectionProviderLoggingTest.cs
@@ -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(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(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(sink.Writes[0].State);
+ Assert.IsType(sink.Writes[1].State);
- var actionDescriptorValues = sink.Writes[3].State as ActionDescriptorValues;
+ var actionDescriptorValues = Assert.IsType(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(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(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();
- 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(),
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionSelectorTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionSelectorTests.cs
index 745cf1ee39..2cd5e37b66 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionSelectorTests.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionSelectorTests.cs
@@ -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(),
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultControllerActivatorTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultControllerActivatorTest.cs
index 03d9400313..e349e416ee 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultControllerActivatorTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultControllerActivatorTest.cs
@@ -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();
- var httpContext = new Mock();
- 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.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>().Value = bindingContext;
-
- var httpContext = new Mock();
- 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();
-
- var httpContext = new Mock();
- 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.SetupGet(c => c.Response)
- .Returns(Mock.Of());
- 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();
- services.Setup(s => s.GetService(typeof(IUrlHelper)))
- .Returns(Mock.Of());
- services.Setup(s => s.GetService(typeof(IModelMetadataProvider)))
- .Returns(new EmptyModelMetadataProvider());
- services
- .Setup(s => s.GetService(typeof(IScopedInstance)))
- .Returns(new MockScopedInstance());
- 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(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(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
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultControllerFactoryTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultControllerFactoryTest.cs
index 6a40b3db9b..a2051abd59 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultControllerFactoryTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultControllerFactoryTest.cs
@@ -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());
+ var httpContext = new DefaultHttpContext();
+ var actionContext = new ActionContext(httpContext,
+ new RouteData(),
+ actionDescriptor);
+
+ // Act and Assert
+ var ex = Assert.Throws(() =>
+ 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();
+ 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(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(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(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>().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(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();
+
+ 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(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();
+
+ 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(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(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(() => 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(),
- Mock.Of(),
- Mock.Of());
-
+ var factory = new DefaultControllerFactory(Mock.Of());
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(),
- Mock.Of(),
- Mock.Of());
-
- var controller = new Object();
+ var factory = new DefaultControllerFactory(Mock.Of());
+ var controller = new object();
// Act + Assert (does not throw)
factory.ReleaseController(controller);
}
+ private IServiceProvider GetServices()
+ {
+ var services = new Mock();
+ services.Setup(s => s.GetService(typeof(IUrlHelper)))
+ .Returns(Mock.Of());
+ 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)))
+ .Returns(new MockScopedInstance());
+ 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
+ {
+
+ }
}
}
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/DefaultControllerTypeProviderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultControllerTypeProviderTest.cs
new file mode 100644
index 0000000000..04357a1a0a
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/DefaultControllerTypeProviderTest.cs
@@ -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).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 : Mvc.Controller
+ {
+ }
+
+ public class DerivedGenericController : OpenGenericController
+ {
+ }
+
+ public interface IController
+ {
+ }
+
+ public class NoSuffix : Mvc.Controller
+ {
+ }
+
+ public class PocoController
+ {
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ServiceBasedControllerActivatorTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ServiceBasedControllerActivatorTest.cs
new file mode 100644
index 0000000000..bc2a5fc86e
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ServiceBasedControllerActivatorTest.cs
@@ -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(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();
+ 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(
+ () => activator.Create(actionContext, typeof(DIController)));
+ Assert.Equal(expected, ex.Message);
+ }
+
+ private class DIController : Controller
+ {
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/StaticAssemblyProvider.cs b/test/Microsoft.AspNet.Mvc.Core.Test/StaticAssemblyProvider.cs
deleted file mode 100644
index 90547adeb8..0000000000
--- a/test/Microsoft.AspNet.Mvc.Core.Test/StaticAssemblyProvider.cs
+++ /dev/null
@@ -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
-{
- ///
- /// An implementation of IAssemblyProvider that provides just this assembly.
- ///
- public class StaticAssemblyProvider : IAssemblyProvider
- {
- public IEnumerable CandidateAssemblies
- {
- get
- {
- yield return typeof(StaticAssemblyProvider).GetTypeInfo().Assembly;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/StaticControllerModelBuilder.cs b/test/Microsoft.AspNet.Mvc.Core.Test/StaticControllerModelBuilder.cs
deleted file mode 100644
index 1ea00f6030..0000000000
--- a/test/Microsoft.AspNet.Mvc.Core.Test/StaticControllerModelBuilder.cs
+++ /dev/null
@@ -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
-{
- ///
- /// An implementation of StaticControllerModelBuilder that only allows controllers
- /// from a fixed set of types.
- ///
- public class StaticControllerModelBuilder : DefaultControllerModelBuilder
- {
- public StaticControllerModelBuilder(params TypeInfo[] controllerTypes)
- : base(new DefaultActionModelBuilder(), new NullLoggerFactory())
- {
- ControllerTypes = new List(controllerTypes ?? Enumerable.Empty());
- }
-
- public List ControllerTypes { get; private set; }
-
- protected override bool IsController([NotNull] TypeInfo typeInfo)
- {
- return ControllerTypes.Contains(typeInfo);
- }
- }
-}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/DefaultViewComponentSelectorTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/DefaultViewComponentSelectorTest.cs
index 759cb88fd2..e8edbacf42 100644
--- a/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/DefaultViewComponentSelectorTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Core.Test/ViewComponents/DefaultViewComponentSelectorTest.cs
@@ -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;
+ }
}
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ActivatorTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ActivatorTests.cs
index dd18dca5ec..5b3190feea 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/ActivatorTests.cs
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ActivatorTests.cs
@@ -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");
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ControllerFromServicesTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ControllerFromServicesTests.cs
new file mode 100644
index 0000000000..bf1c47962e
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ControllerFromServicesTests.cs
@@ -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 _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);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/Logging/LoggingStartupTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/Logging/LoggingStartupTest.cs
index f4bfe81024..9d9e13528b 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/Logging/LoggingStartupTest.cs
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/Logging/LoggingStartupTest.cs
@@ -40,31 +40,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
}
}
- [Fact]
- public async Task IsControllerValues_LoggedAtStartup()
- {
- // Arrange and Act
- var logs = await GetLogsByDataTypeAsync();
-
- // 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()
{
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json
index ead5aa95ad..00b56f537f 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json
@@ -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",
diff --git a/test/Microsoft.AspNet.Mvc.Test/MvcServiceCollectionExtensionsTest.cs b/test/Microsoft.AspNet.Mvc.Test/MvcServiceCollectionExtensionsTest.cs
new file mode 100644
index 0000000000..bde633b0ca
--- /dev/null
+++ b/test/Microsoft.AspNet.Mvc.Test/MvcServiceCollectionExtensionsTest.cs
@@ -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(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(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 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
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs
index 68c1fb0e95..4836a1c585 100644
--- a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs
+++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerActionDiscoveryTest.cs
@@ -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 CreateProvider()
{
- var assemblyProvider = new Mock();
- 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();
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 ControllerTypes
{
- return
- typeInfo.Namespace == "System.Web.Http.TestControllers" &&
- base.IsController(typeInfo);
+ get
+ {
+ return base.ControllerTypes
+ .Where(typeInfo => typeInfo.Namespace == "System.Web.Http.TestControllers");
+ }
}
}
}
diff --git a/test/WebSites/ActivatorWebSite/Controllers/PlainController.cs b/test/WebSites/ActivatorWebSite/Controllers/PlainController.cs
index b634f33e33..94c52f33fb 100644
--- a/test/WebSites/ActivatorWebSite/Controllers/PlainController.cs
+++ b/test/WebSites/ActivatorWebSite/Controllers/PlainController.cs
@@ -8,7 +8,7 @@ namespace ActivatorWebSite
{
public class PlainController
{
- [Activate]
+ [FromServices]
public MyService Service { get; set; }
[Activate]
diff --git a/test/WebSites/ControllersFromServicesClassLibrary/ControllerWithConstructorInjection.cs b/test/WebSites/ControllersFromServicesClassLibrary/ControllerWithConstructorInjection.cs
new file mode 100644
index 0000000000..4aca4b7ea8
--- /dev/null
+++ b/test/WebSites/ControllersFromServicesClassLibrary/ControllerWithConstructorInjection.cs
@@ -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 };
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/WebSites/ControllersFromServicesClassLibrary/ControllersFromServicesClassLibrary.kproj b/test/WebSites/ControllersFromServicesClassLibrary/ControllersFromServicesClassLibrary.kproj
new file mode 100644
index 0000000000..95b3103159
--- /dev/null
+++ b/test/WebSites/ControllersFromServicesClassLibrary/ControllersFromServicesClassLibrary.kproj
@@ -0,0 +1,20 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ 551dc89e-2a13-4cf2-83d7-1add802443d5
+
+
+
+
+
+
+
+ 2.0
+
+
+
\ No newline at end of file
diff --git a/test/WebSites/ControllersFromServicesClassLibrary/EmployeeRecords.cs b/test/WebSites/ControllersFromServicesClassLibrary/EmployeeRecords.cs
new file mode 100644
index 0000000000..df505a0172
--- /dev/null
+++ b/test/WebSites/ControllersFromServicesClassLibrary/EmployeeRecords.cs
@@ -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);
+ }
+
+ }
+}
diff --git a/test/WebSites/ControllersFromServicesClassLibrary/GenericController.cs b/test/WebSites/ControllersFromServicesClassLibrary/GenericController.cs
new file mode 100644
index 0000000000..01111015cc
--- /dev/null
+++ b/test/WebSites/ControllersFromServicesClassLibrary/GenericController.cs
@@ -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 : Controller
+ {
+ [HttpGet("/not-discovered/generic")]
+ public IActionResult Index()
+ {
+ return new EmptyResult();
+ }
+ }
+}
diff --git a/test/WebSites/ControllersFromServicesClassLibrary/NestedControllerOwner.cs b/test/WebSites/ControllersFromServicesClassLibrary/NestedControllerOwner.cs
new file mode 100644
index 0000000000..858e2bfc61
--- /dev/null
+++ b/test/WebSites/ControllersFromServicesClassLibrary/NestedControllerOwner.cs
@@ -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();
+ }
+ }
+ }
+}
diff --git a/test/WebSites/ControllersFromServicesClassLibrary/QueryValueService.cs b/test/WebSites/ControllersFromServicesClassLibrary/QueryValueService.cs
new file mode 100644
index 0000000000..1a3b1e63b0
--- /dev/null
+++ b/test/WebSites/ControllersFromServicesClassLibrary/QueryValueService.cs
@@ -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"];
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/WebSites/ControllersFromServicesClassLibrary/TimeScheduleController.cs b/test/WebSites/ControllersFromServicesClassLibrary/TimeScheduleController.cs
new file mode 100644
index 0000000000..5ce767eeb5
--- /dev/null
+++ b/test/WebSites/ControllersFromServicesClassLibrary/TimeScheduleController.cs
@@ -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 };
+ }
+ }
+}
diff --git a/test/WebSites/ControllersFromServicesClassLibrary/project.json b/test/WebSites/ControllersFromServicesClassLibrary/project.json
new file mode 100644
index 0000000000..1c8a133867
--- /dev/null
+++ b/test/WebSites/ControllersFromServicesClassLibrary/project.json
@@ -0,0 +1,9 @@
+{
+ "dependencies": {
+ "Microsoft.AspNet.Mvc": "6.0.0-*",
+ },
+ "frameworks": {
+ "aspnet50": { },
+ "aspnetcore50": { }
+ }
+}
\ No newline at end of file
diff --git a/test/WebSites/ControllersFromServicesWebSite/ControllersFromServicesWebSite.kproj b/test/WebSites/ControllersFromServicesWebSite/ControllersFromServicesWebSite.kproj
new file mode 100644
index 0000000000..0e4d50e9d2
--- /dev/null
+++ b/test/WebSites/ControllersFromServicesWebSite/ControllersFromServicesWebSite.kproj
@@ -0,0 +1,20 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ 983741b2-4424-4ed1-9b03-7675a67230c8
+
+
+
+
+
+
+ 2.0
+ 42994
+
+
+
\ No newline at end of file
diff --git a/test/WebSites/ControllersFromServicesWebSite/NotInServicesController.cs b/test/WebSites/ControllersFromServicesWebSite/NotInServicesController.cs
new file mode 100644
index 0000000000..96c27229b7
--- /dev/null
+++ b/test/WebSites/ControllersFromServicesWebSite/NotInServicesController.cs
@@ -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();
+ }
+ }
+}
diff --git a/test/WebSites/ControllersFromServicesWebSite/Startup.cs b/test/WebSites/ControllersFromServicesWebSite/Startup.cs
new file mode 100644
index 0000000000..95fb0b8c80
--- /dev/null
+++ b/test/WebSites/ControllersFromServicesWebSite/Startup.cs
@@ -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();
+
+#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();
+#endif
+ });
+
+ app.UseMvc(routes =>
+ {
+ routes.MapRoute("default", "{controller}/{action}/{id}");
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/WebSites/ControllersFromServicesWebSite/ViewData.cshtml b/test/WebSites/ControllersFromServicesWebSite/ViewData.cshtml
new file mode 100644
index 0000000000..c864b370c8
--- /dev/null
+++ b/test/WebSites/ControllersFromServicesWebSite/ViewData.cshtml
@@ -0,0 +1 @@
+@ViewBag.Value
\ No newline at end of file
diff --git a/test/WebSites/ControllersFromServicesWebSite/project.json b/test/WebSites/ControllersFromServicesWebSite/project.json
new file mode 100644
index 0000000000..5026f392c7
--- /dev/null
+++ b/test/WebSites/ControllersFromServicesWebSite/project.json
@@ -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"
+}
\ No newline at end of file
diff --git a/test/WebSites/ControllersFromServicesWebSite/wwwroot/readme.md b/test/WebSites/ControllersFromServicesWebSite/wwwroot/readme.md
new file mode 100644
index 0000000000..ad8de8159e
--- /dev/null
+++ b/test/WebSites/ControllersFromServicesWebSite/wwwroot/readme.md
@@ -0,0 +1 @@
+Functional test site for verifying that controllers registered as services can be consumed by DefaultControllerFactory.
\ No newline at end of file
diff --git a/test/WebSites/ModelBindingWebSite/Controllers/RoundtripController.cs b/test/WebSites/ModelBindingWebSite/Controllers/RoundtripController.cs
index 8f30225bcf..7e8bdfcd2c 100644
--- a/test/WebSites/ModelBindingWebSite/Controllers/RoundtripController.cs
+++ b/test/WebSites/ModelBindingWebSite/Controllers/RoundtripController.cs
@@ -14,7 +14,7 @@ namespace ModelBindingWebSite.Controllers
private IHtmlHelper _personHelper;
private bool _activated;
- [Activate]
+ [FromServices]
public IHtmlHelper PersonHelper
{
get
diff --git a/test/WebSites/MvcTagHelpersWebSite/Controllers/Catalog_CacheTagHelperController.cs b/test/WebSites/MvcTagHelpersWebSite/Controllers/Catalog_CacheTagHelperController.cs
index 3fb0549183..51ba703cf4 100644
--- a/test/WebSites/MvcTagHelpersWebSite/Controllers/Catalog_CacheTagHelperController.cs
+++ b/test/WebSites/MvcTagHelpersWebSite/Controllers/Catalog_CacheTagHelperController.cs
@@ -8,7 +8,7 @@ namespace MvcTagHelpersWebSite.Controllers
{
public class Catalog_CacheTagHelperController : Controller
{
- [Activate]
+ [FromServices]
public ProductsService ProductsService { get; set; }
[HttpGet("/catalog")]
diff --git a/test/WebSites/RequestServicesWebSite/Controllers/RequestScopedServiceController.cs b/test/WebSites/RequestServicesWebSite/Controllers/RequestScopedServiceController.cs
index 2586890afa..dcaeb61d32 100644
--- a/test/WebSites/RequestServicesWebSite/Controllers/RequestScopedServiceController.cs
+++ b/test/WebSites/RequestServicesWebSite/Controllers/RequestScopedServiceController.cs
@@ -8,7 +8,7 @@ namespace RequestServicesWebSite
[Route("RequestScoped/[action]")]
public class RequestScopedServiceController
{
- [Activate]
+ [FromServices]
public RequestIdService RequestIdService { get; set; }
[HttpGet]
diff --git a/test/WebSites/WebApiCompatShimWebSite/Controllers/BasicApiController.cs b/test/WebSites/WebApiCompatShimWebSite/Controllers/BasicApiController.cs
index 16a04e36f9..141779a979 100644
--- a/test/WebSites/WebApiCompatShimWebSite/Controllers/BasicApiController.cs
+++ b/test/WebSites/WebApiCompatShimWebSite/Controllers/BasicApiController.cs
@@ -15,7 +15,7 @@ namespace WebApiCompatShimWebSite
{
public class BasicApiController : ApiController
{
- [Activate]
+ [FromServices]
public IOptions OptionsAccessor { get; set; }
// Verifies property activation