diff --git a/Mvc.sln b/Mvc.sln
index 5506ce9d33..f5b4c9e0f9 100644
--- a/Mvc.sln
+++ b/Mvc.sln
@@ -37,6 +37,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Functi
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "BasicWebSite", "test\WebSites\BasicWebSite\BasicWebSite.kproj", "{34DF1487-12C6-476C-BE0A-F31DF1939AE5}"
EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ActivatorWebSite", "test\WebSites\ActivatorWebSite\ActivatorWebSite.kproj", "{DB79BCBA-9538-4A53-87D9-77728E2BAA39}"
+EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "InlineConstraintsWebSite", "test\WebSites\InlineConstraintsWebSite\InlineConstraintsWebSite.kproj", "{EA34877F-1AC1-42B7-B4E6-15A093F40CAE}"
EndProject
Global
@@ -179,6 +181,16 @@ Global
{34DF1487-12C6-476C-BE0A-F31DF1939AE5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{34DF1487-12C6-476C-BE0A-F31DF1939AE5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{34DF1487-12C6-476C-BE0A-F31DF1939AE5}.Release|x86.ActiveCfg = Release|Any CPU
+ {DB79BCBA-9538-4A53-87D9-77728E2BAA39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DB79BCBA-9538-4A53-87D9-77728E2BAA39}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DB79BCBA-9538-4A53-87D9-77728E2BAA39}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {DB79BCBA-9538-4A53-87D9-77728E2BAA39}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {DB79BCBA-9538-4A53-87D9-77728E2BAA39}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DB79BCBA-9538-4A53-87D9-77728E2BAA39}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DB79BCBA-9538-4A53-87D9-77728E2BAA39}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DB79BCBA-9538-4A53-87D9-77728E2BAA39}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {DB79BCBA-9538-4A53-87D9-77728E2BAA39}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {DB79BCBA-9538-4A53-87D9-77728E2BAA39}.Release|x86.ActiveCfg = Release|Any CPU
{EA34877F-1AC1-42B7-B4E6-15A093F40CAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA34877F-1AC1-42B7-B4E6-15A093F40CAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA34877F-1AC1-42B7-B4E6-15A093F40CAE}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -208,6 +220,7 @@ Global
{16703B76-C9F7-4C75-AE6C-53D92E308E3C} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{323D0C04-B518-4A8F-8A8E-3546AD153D34} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{34DF1487-12C6-476C-BE0A-F31DF1939AE5} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
+ {DB79BCBA-9538-4A53-87D9-77728E2BAA39} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{EA34877F-1AC1-42B7-B4E6-15A093F40CAE} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
EndGlobalSection
EndGlobal
diff --git a/samples/MvcSample.Web/Home2Controller.cs b/samples/MvcSample.Web/Home2Controller.cs
index 214e5fe1a2..1ec20284c8 100644
--- a/samples/MvcSample.Web/Home2Controller.cs
+++ b/samples/MvcSample.Web/Home2Controller.cs
@@ -8,15 +8,12 @@ namespace MvcSample.Web.RandomNameSpace
{
private User _user = new User() { Name = "User Name", Address = "Home Address" };
- public HttpContext Context
+ [Activate]
+ public HttpResponse Response
{
- get
- {
- return ActionContext.HttpContext;
- }
+ get; set;
}
- // The property ActionContext gets injected by InitializeController from DefaultControllerFactory.
public ActionContext ActionContext { get; set; }
public string Index()
@@ -42,7 +39,7 @@ namespace MvcSample.Web.RandomNameSpace
public void Raw()
{
- Context.Response.WriteAsync("Hello World raw");
+ Response.WriteAsync("Hello World raw");
}
public ActionResult UserJson()
diff --git a/src/Microsoft.AspNet.Mvc.Core/ActivateAttribute.cs b/src/Microsoft.AspNet.Mvc.Core/ActivateAttribute.cs
new file mode 100644
index 0000000000..463e55d774
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/ActivateAttribute.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 System;
+
+namespace Microsoft.AspNet.Mvc
+{
+ ///
+ /// Specifies that a property or parameter value should be initialized via the dependency injection
+ /// container for activated types.
+ ///
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false)]
+ public sealed class ActivateAttribute : Attribute
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Core/Controller.cs b/src/Microsoft.AspNet.Mvc.Core/Controller.cs
index 6ae4224016..9e7b5e6d2b 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Controller.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Controller.cs
@@ -15,15 +15,6 @@ namespace Microsoft.AspNet.Mvc
public class Controller : IActionFilter, IAsyncActionFilter
{
private DynamicViewData _viewBag;
- private IServiceProvider _serviceProvider;
- private IViewEngine _viewEngine;
-
- [NonAction]
- public virtual void Initialize(IServiceProvider serviceProvider, IViewEngine viewEngine)
- {
- _serviceProvider = serviceProvider;
- _viewEngine = viewEngine;
- }
public HttpContext Context
{
@@ -41,8 +32,16 @@ namespace Microsoft.AspNet.Mvc
}
}
+ [Activate]
public ActionContext ActionContext { get; set; }
+ [Activate]
+ public IServiceProvider ServiceProvider { get; set; }
+
+ [Activate]
+ public IViewEngine ViewEngine { get; set; }
+
+ [Activate]
public IUrlHelper Url { get; set; }
public IPrincipal User
@@ -58,6 +57,7 @@ namespace Microsoft.AspNet.Mvc
}
}
+ [Activate]
public ViewDataDictionary ViewData { get; set; }
public dynamic ViewBag
@@ -122,7 +122,7 @@ namespace Microsoft.AspNet.Mvc
ViewData.Model = model;
}
- return new ViewResult(_serviceProvider, _viewEngine)
+ return new ViewResult(ServiceProvider, ViewEngine)
{
ViewName = viewName,
ViewData = ViewData,
diff --git a/src/Microsoft.AspNet.Mvc.Core/DefaultControllerActivator.cs b/src/Microsoft.AspNet.Mvc.Core/DefaultControllerActivator.cs
new file mode 100644
index 0000000000..87918e6172
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/DefaultControllerActivator.cs
@@ -0,0 +1,131 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+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
+{
+ ///
+ /// Represents the that is registered by default.
+ ///
+ public class DefaultControllerActivator : IControllerActivator
+ {
+ private readonly Func _getPropertiesToActivate;
+ private readonly Func _createActivateInfo;
+ private readonly ReadOnlyDictionary> _valueAccessorLookup;
+ private readonly ConcurrentDictionary _injectActions;
+
+ ///
+ /// Initializes a new instance of the DefaultControllerActivator class.
+ ///
+ public DefaultControllerActivator()
+ {
+ _valueAccessorLookup = CreateValueAccessorLookup();
+ _getPropertiesToActivate = GetPropertiesToActivate;
+ _createActivateInfo = CreateActivateInfo;
+ _injectActions = new ConcurrentDictionary();
+ }
+
+ ///
+ /// Activates the specified controller by using the specified action context.
+ ///
+ /// The controller to activate.
+ /// The context of the executing action.
+ public 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 ReadOnlyDictionary> 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.GetService(),
+ context.ModelState);
+ }
+ }
+ };
+ return new ReadOnlyDictionary>(dictionary);
+ }
+
+ private PropertyActivator[] GetPropertiesToActivate(Type controllerType)
+ {
+ var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
+ return controllerType.GetProperties(bindingFlags)
+ .Where(property => property.IsDefined(typeof(ActivateAttribute)) &&
+ property.GetSetMethod(nonPublic: true) != null)
+ .Select(_createActivateInfo)
+ .ToArray();
+ }
+
+ private PropertyActivator CreateActivateInfo(PropertyInfo property)
+ {
+ Func valueAccessor;
+ if (!_valueAccessorLookup.TryGetValue(property.PropertyType, out valueAccessor))
+ {
+ valueAccessor = (actionContext) =>
+ {
+ var serviceProvider = actionContext.HttpContext.RequestServices;
+ return serviceProvider.GetService(property.PropertyType);
+ };
+ }
+
+ return new PropertyActivator(property,
+ valueAccessor);
+ }
+
+ private sealed class PropertyActivator
+ {
+ private readonly PropertyInfo _propertyInfo;
+ private readonly Func _valueAccessor;
+ private readonly Action