Skip to content

Commit 144c1d3

Browse files
committed
Fix aspnet#2151 - Part 4 remove [Activate] support from controllers.
This change completely removes [Activate]. In a controller, you should constructor injection or [FromServices] to access services. To access context items (ActionContext, ActionBindingContext, root ViewDataDictionary) you should use the respected attribute class. We'd like to consider streamlining this further in the future by getting down to a single injectable context for controllers, but for now this will have to do.
1 parent af5322e commit 144c1d3

File tree

14 files changed

+147
-156
lines changed

14 files changed

+147
-156
lines changed

samples/MvcSample.Web/ApiExplorerController.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ namespace MvcSample.Web
99
[Route("ApiExplorer")]
1010
public class ApiExplorerController : Controller
1111
{
12-
[Activate]
13-
public IApiDescriptionGroupCollectionProvider Provider { get; set; }
12+
public ApiExplorerController(IApiDescriptionGroupCollectionProvider provider)
13+
{
14+
Provider = provider;
15+
}
16+
17+
public IApiDescriptionGroupCollectionProvider Provider { get; }
1418

1519
[HttpGet]
1620
public IActionResult All()

samples/MvcSample.Web/Home2Controller.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,10 @@ public class Home2Controller
1212
{
1313
private User _user = new User() { Name = "User Name", Address = "Home Address" };
1414

15-
[Activate]
16-
public HttpResponse Response
17-
{
18-
get; set;
19-
}
15+
[ActionContext]
16+
public ActionContext ActionContext { get; set; }
17+
18+
public HttpResponse Response => ActionContext.HttpContext.Response;
2019

2120
public string Index()
2221
{
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
6+
namespace Microsoft.AspNet.Mvc
7+
{
8+
/// <summary>
9+
/// Specifies that a controller property should be set with the current
10+
/// <see cref="ActionBindingContext"/> when creating the controller. The property must have a public
11+
/// set method.
12+
/// </summary>
13+
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
14+
public class ActionBindingContextAttribute : Attribute
15+
{
16+
}
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
6+
namespace Microsoft.AspNet.Mvc
7+
{
8+
/// <summary>
9+
/// Specifies that a controller property should be set with the current
10+
/// <see cref="ActionContext"/> when creating the controller. The property must have a public
11+
/// set method.
12+
/// </summary>
13+
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
14+
public class ActionContextAttribute : Attribute
15+
{
16+
}
17+
}

src/Microsoft.AspNet.Mvc.Core/ActivateAttribute.cs

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/Microsoft.AspNet.Mvc.Core/Controller.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public ModelStateDictionary ModelState
101101
/// <see cref="IControllerActivator"/> activates this property while activating controllers. If user codes
102102
/// directly instantiate controllers, the getter returns an empty <see cref="Mvc.ActionContext"/>.
103103
/// </remarks>
104-
[Activate]
104+
[ActionContext]
105105
public ActionContext ActionContext
106106
{
107107
get
@@ -124,7 +124,7 @@ public ActionContext ActionContext
124124
/// <summary>
125125
/// Gets or sets the <see cref="ActionBindingContext"/>.
126126
/// </summary>
127-
[Activate]
127+
[ActionBindingContext]
128128
public ActionBindingContext BindingContext { get; set; }
129129

130130
/// <summary>
@@ -161,7 +161,7 @@ public ClaimsPrincipal User
161161
/// However, when controllers are directly instantiated in user codes, this property is initialized with
162162
/// <see cref="EmptyModelMetadataProvider"/>.
163163
/// </remarks>
164-
[Activate]
164+
[ViewDataDictionary]
165165
public ViewDataDictionary ViewData
166166
{
167167
get

src/Microsoft.AspNet.Mvc.Core/DefaultControllerFactory.cs

Lines changed: 30 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using System.Collections.Generic;
77
using System.Linq;
88
using System.Reflection;
9-
using Microsoft.AspNet.Http;
109
using Microsoft.AspNet.Mvc.Core;
1110
using Microsoft.AspNet.Mvc.ModelBinding;
1211
using Microsoft.Framework.DependencyInjection;
@@ -20,10 +19,8 @@ namespace Microsoft.AspNet.Mvc
2019
public class DefaultControllerFactory : IControllerFactory
2120
{
2221
private readonly IControllerActivator _controllerActivator;
23-
private readonly IDictionary<Type, Func<ActionContext, object>> _valueAccessorLookup;
2422
private readonly ConcurrentDictionary<Type, PropertyActivator<ActionContext>[]> _activateActions;
2523
private readonly Func<Type, PropertyActivator<ActionContext>[]> _getPropertiesToActivate;
26-
private readonly Func<Type, Func<ActionContext, object>> _getRequiredService = GetRequiredService;
2724

2825
/// <summary>
2926
/// Initializes a new instance of <see cref="DefaultControllerFactory"/>.
@@ -33,7 +30,7 @@ public class DefaultControllerFactory : IControllerFactory
3330
public DefaultControllerFactory(IControllerActivator controllerActivator)
3431
{
3532
_controllerActivator = controllerActivator;
36-
_valueAccessorLookup = CreateValueAccessorLookup();
33+
3734
_activateActions = new ConcurrentDictionary<Type, PropertyActivator<ActionContext>[]>();
3835
_getPropertiesToActivate = GetPropertiesToActivate;
3936
}
@@ -98,8 +95,9 @@ public virtual void ReleaseController(object controller)
9895
protected virtual void ActivateProperties([NotNull] object controller, [NotNull] ActionContext context)
9996
{
10097
var controllerType = controller.GetType();
101-
var propertiesToActivate = _activateActions.GetOrAdd(controllerType,
102-
_getPropertiesToActivate);
98+
var propertiesToActivate = _activateActions.GetOrAdd(
99+
controllerType,
100+
_getPropertiesToActivate);
103101

104102
for (var i = 0; i < propertiesToActivate.Length; i++)
105103
{
@@ -108,79 +106,40 @@ protected virtual void ActivateProperties([NotNull] object controller, [NotNull]
108106
}
109107
}
110108

111-
/// <summary>
112-
/// Returns a <see cref="IDictionary{TKey, TValue}"/> of property types to delegates used to activate
113-
/// controller properties annotated with <see cref="ActivateAttribute"/>.
114-
/// </summary>
115-
/// <returns>A dictionary containing the property type to activator delegate mapping.</returns>
116-
/// <remarks>Override this method to provide custom activation behavior for controller properties
117-
/// annotated with <see cref="ActivateAttribute"/>.</remarks>
118-
protected virtual IDictionary<Type, Func<ActionContext, object>> CreateValueAccessorLookup()
119-
{
120-
var dictionary = new Dictionary<Type, Func<ActionContext, object>>
121-
{
122-
{ typeof(ActionContext), (context) => context },
123-
{ typeof(HttpContext), (context) => context.HttpContext },
124-
{ typeof(HttpRequest), (context) => context.HttpContext.Request },
125-
{ typeof(HttpResponse), (context) => context.HttpContext.Response },
126-
{
127-
typeof(ViewDataDictionary),
128-
(context) =>
129-
{
130-
var serviceProvider = context.HttpContext.RequestServices;
131-
return new ViewDataDictionary(
132-
serviceProvider.GetRequiredService<IModelMetadataProvider>(),
133-
context.ModelState);
134-
}
135-
},
136-
{
137-
typeof(ActionBindingContext),
138-
(context) =>
139-
{
140-
var serviceProvider = context.HttpContext.RequestServices;
141-
var accessor = serviceProvider.GetRequiredService<IScopedInstance<ActionBindingContext>>();
142-
return accessor.Value;
143-
}
144-
}
145-
};
146-
147-
return dictionary;
148-
}
149-
150109
private PropertyActivator<ActionContext>[] GetPropertiesToActivate(Type type)
151110
{
152-
var activatorsForActivateProperties = PropertyActivator<ActionContext>.GetPropertiesToActivate(
153-
type,
154-
typeof(ActivateAttribute),
155-
CreateActivateInfo);
156-
return activatorsForActivateProperties;
157-
}
158-
159-
private PropertyActivator<ActionContext> CreateActivateInfo(
160-
PropertyInfo property)
161-
{
162-
Func<ActionContext, object> valueAccessor;
163-
if (!_valueAccessorLookup.TryGetValue(property.PropertyType, out valueAccessor))
164-
{
165-
var message = Resources.FormatControllerFactory_PropertyCannotBeActivated(
166-
property.Name,
167-
property.DeclaringType.FullName);
168-
throw new InvalidOperationException(message);
169-
}
170-
171-
return new PropertyActivator<ActionContext>(property, valueAccessor);
111+
IEnumerable<PropertyActivator<ActionContext>> activators;
112+
activators = PropertyActivator<ActionContext>.GetPropertiesToActivate(
113+
type,
114+
typeof(ActionContextAttribute),
115+
p => new PropertyActivator<ActionContext>(p, c => c));
116+
117+
activators = activators.Concat(PropertyActivator<ActionContext>.GetPropertiesToActivate(
118+
type,
119+
typeof(ActionBindingContextAttribute),
120+
p => new PropertyActivator<ActionContext>(p, GetActionBindingContext)));
121+
122+
activators = activators.Concat(PropertyActivator<ActionContext>.GetPropertiesToActivate(
123+
type,
124+
typeof(ViewDataDictionaryAttribute),
125+
p => new PropertyActivator<ActionContext>(p, GetViewDataDictionary)));
126+
127+
return activators.ToArray();
172128
}
173129

174-
private PropertyActivator<ActionContext> CreateFromServicesInfo(
175-
PropertyInfo property)
130+
private static ActionBindingContext GetActionBindingContext(ActionContext context)
176131
{
177-
var valueAccessor = _getRequiredService(property.PropertyType);
178-
return new PropertyActivator<ActionContext>(property, valueAccessor);
132+
var serviceProvider = context.HttpContext.RequestServices;
133+
var accessor = serviceProvider.GetRequiredService<IScopedInstance<ActionBindingContext>>();
134+
return accessor.Value;
179135
}
180136

181-
private static Func<ActionContext, object> GetRequiredService(Type propertyType)
137+
private static ViewDataDictionary GetViewDataDictionary(ActionContext context)
182138
{
183-
return actionContext => actionContext.HttpContext.RequestServices.GetRequiredService(propertyType);
139+
var serviceProvider = context.HttpContext.RequestServices;
140+
return new ViewDataDictionary(
141+
serviceProvider.GetRequiredService<IModelMetadataProvider>(),
142+
context.ModelState);
184143
}
185144
}
186145
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
6+
namespace Microsoft.AspNet.Mvc
7+
{
8+
/// <summary>
9+
/// Specifies that a controller property should be set with the current
10+
/// <see cref="ViewDataDictionary"/> when creating the controller. The property must have a public
11+
/// set method.
12+
/// </summary>
13+
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
14+
public class ViewDataDictionaryAttribute : Attribute
15+
{
16+
}
17+
}

src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ public abstract class ApiController : IDisposable
2727
/// Gets the action context.
2828
/// </summary>
2929
/// <remarks>The setter is intended for unit testing purposes only.</remarks>
30-
[Activate]
30+
[ActionContext]
3131
public ActionContext ActionContext { get; set; }
3232

3333
/// <summary>
3434
/// Gets the <see cref="ActionBindingContext"/>.
3535
/// </summary>
3636
/// <remarks>The setter is intended for unit testing purposes only.</remarks>
37-
[Activate]
37+
[ActionBindingContext]
3838
public ActionBindingContext BindingContext { get; set; }
3939

4040
/// <summary>

0 commit comments

Comments
 (0)