Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Commit 0c5a702

Browse files
committed
Changing when controllers are created
This change moves controller creation to the stage immediately before model binding. The controller will be disposed/released before Resource Filters run their 'OnResourceExecuted' method. Previously the controller's lifetime surrounded all filter invocation. Additionally, the Controller property is now gone from ActionContext, and is moved to the 4 filter contexts that should have access to it Action*Context and Result*Context.
1 parent e1069db commit 0c5a702

15 files changed

+184
-110
lines changed

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ public ActionContext([NotNull] ActionContext actionContext)
1414
: this(actionContext.HttpContext, actionContext.RouteData, actionContext.ActionDescriptor)
1515
{
1616
ModelState = actionContext.ModelState;
17-
Controller = actionContext.Controller;
1817
}
1918

2019
public ActionContext([NotNull] RouteContext routeContext, [NotNull] ActionDescriptor actionDescriptor)
@@ -39,10 +38,5 @@ public ActionContext([NotNull] HttpContext httpContext,
3938
public ModelStateDictionary ModelState { get; private set; }
4039

4140
public ActionDescriptor ActionDescriptor { get; private set; }
42-
43-
/// <summary>
44-
/// The controller is available only after the controller factory runs.
45-
/// </summary>
46-
public object Controller { get; set; }
4741
}
4842
}

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

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,29 +50,24 @@ public ControllerActionInvoker(
5050
}
5151
}
5252

53-
public async override Task InvokeAsync()
53+
protected override object CreateInstance()
5454
{
5555
// The binding context is used in activation
5656
Debug.Assert(ActionBindingContext != null);
57-
var controller = _controllerFactory.CreateController(ActionContext);
57+
return _controllerFactory.CreateController(ActionContext);
58+
}
5859

59-
try
60-
{
61-
ActionContext.Controller = controller;
62-
await base.InvokeAsync();
63-
}
64-
finally
65-
{
66-
_controllerFactory.ReleaseController(ActionContext.Controller);
67-
}
60+
protected override void ReleaseInstance(object instance)
61+
{
62+
_controllerFactory.ReleaseController(instance);
6863
}
6964

7065
protected override async Task<IActionResult> InvokeActionAsync(ActionExecutingContext actionExecutingContext)
7166
{
7267
var actionMethodInfo = _descriptor.MethodInfo;
7368
var actionReturnValue = await ControllerActionExecutor.ExecuteAsync(
7469
actionMethodInfo,
75-
ActionContext.Controller,
70+
actionExecutingContext.Controller,
7671
actionExecutingContext.ActionArguments);
7772

7873
var actionResult = CreateActionResult(

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ public object CreateController(ActionContext actionContext)
3737
_serviceProvider,
3838
actionDescriptor.ControllerTypeInfo.AsType());
3939

40-
actionContext.Controller = controller;
4140
_controllerActivator.Activate(controller, actionContext);
4241

4342
return controller;

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

Lines changed: 67 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@ public FilterActionInvoker(
5555
_modelValidatorProviderProvider = modelValidatorProviderProvider;
5656
_valueProviderFactoryProvider = valueProviderFactoryProvider;
5757
_actionBindingContextAccessor = actionBindingContextAccessor;
58-
59-
ActionBindingContext = new ActionBindingContext();
6058
}
6159

6260
protected ActionContext ActionContext { get; private set; }
@@ -73,6 +71,21 @@ private set
7371
}
7472
}
7573

74+
protected object Instance { get; private set; }
75+
76+
/// <summary>
77+
/// Called to create an instance of an object which will act as the reciever of the action invocation.
78+
/// </summary>
79+
/// <returns>The constructed instance or <c>null</c>.</returns>
80+
protected abstract object CreateInstance();
81+
82+
/// <summary>
83+
/// Called to create an instance of an object which will act as the reciever of the action invocation.
84+
/// </summary>
85+
/// <param name="instance">The instance to release.</param>
86+
/// <remarks>This method will not be called if <see cref="CreateInstance"/> returns <c>null</c>.</remarks>
87+
protected abstract void ReleaseInstance(object instance);
88+
7689
protected abstract Task<IActionResult> InvokeActionAsync(ActionExecutingContext actionExecutingContext);
7790

7891
protected abstract Task<IDictionary<string, object>> GetActionArgumentsAsync(
@@ -95,7 +108,20 @@ public virtual async Task InvokeAsync()
95108
return;
96109
}
97110

98-
await InvokeAllResourceFiltersAsync();
111+
try
112+
{
113+
await InvokeAllResourceFiltersAsync();
114+
}
115+
finally
116+
{
117+
// Release the instance after all filters have run. We don't need to surround
118+
// Authorizations filters because the instance will be created much later than
119+
// that.
120+
if (Instance != null)
121+
{
122+
ReleaseInstance(Instance);
123+
}
124+
}
99125

100126
// We've reached the end of resource filters. If there's an unhandled exception on the context then
101127
// it should be thrown and middleware has a chance to handle it.
@@ -207,7 +233,7 @@ private async Task<ResourceExecutedContext> InvokeResourceFilterAsync()
207233
if (item.FilterAsync != null)
208234
{
209235
await item.FilterAsync.OnResourceExecutionAsync(
210-
_resourceExecutingContext,
236+
_resourceExecutingContext,
211237
InvokeResourceFilterAsync);
212238

213239
if (_resourceExecutedContext == null)
@@ -384,23 +410,30 @@ private async Task InvokeAllActionFiltersAsync()
384410

385411
Debug.Assert(_resourceExecutingContext != null);
386412

387-
Debug.Assert(ActionBindingContext != null);
413+
ActionBindingContext = new ActionBindingContext();
388414
ActionBindingContext.InputFormatters = _resourceExecutingContext.InputFormatters;
389415
ActionBindingContext.ModelBinder = new CompositeModelBinder(_resourceExecutingContext.ModelBinders);
390416
ActionBindingContext.ValidatorProvider = new CompositeModelValidatorProvider(
391417
_resourceExecutingContext.ValidatorProviders);
392418

393419
var valueProviderFactoryContext = new ValueProviderFactoryContext(
394-
ActionContext.HttpContext,
420+
ActionContext.HttpContext,
395421
ActionContext.RouteData.Values);
396422

397423
ActionBindingContext.ValueProvider = CompositeValueProvider.Create(
398424
_resourceExecutingContext.ValueProviderFactories,
399425
valueProviderFactoryContext);
400426

427+
Instance = CreateInstance();
428+
401429
var arguments = await GetActionArgumentsAsync(ActionContext, ActionBindingContext);
402430

403-
_actionExecutingContext = new ActionExecutingContext(ActionContext, _filters, arguments);
431+
_actionExecutingContext = new ActionExecutingContext(
432+
ActionContext,
433+
_filters,
434+
arguments,
435+
Instance);
436+
404437
await InvokeActionFilterAsync();
405438
}
406439

@@ -429,7 +462,10 @@ private async Task<ActionExecutedContext> InvokeActionFilterAsync()
429462
if (_actionExecutedContext == null)
430463
{
431464
// If we get here then the filter didn't call 'next' indicating a short circuit
432-
_actionExecutedContext = new ActionExecutedContext(_actionExecutingContext, _filters)
465+
_actionExecutedContext = new ActionExecutedContext(
466+
_actionExecutingContext,
467+
_filters,
468+
Instance)
433469
{
434470
Canceled = true,
435471
Result = _actionExecutingContext.Result,
@@ -443,7 +479,10 @@ private async Task<ActionExecutedContext> InvokeActionFilterAsync()
443479
if (_actionExecutingContext.Result != null)
444480
{
445481
// Short-circuited by setting a result.
446-
_actionExecutedContext = new ActionExecutedContext(_actionExecutingContext, _filters)
482+
_actionExecutedContext = new ActionExecutedContext(
483+
_actionExecutingContext,
484+
_filters,
485+
Instance)
447486
{
448487
Canceled = true,
449488
Result = _actionExecutingContext.Result,
@@ -457,7 +496,10 @@ private async Task<ActionExecutedContext> InvokeActionFilterAsync()
457496
else
458497
{
459498
// All action filters have run, execute the action method.
460-
_actionExecutedContext = new ActionExecutedContext(_actionExecutingContext, _filters)
499+
_actionExecutedContext = new ActionExecutedContext(
500+
_actionExecutingContext,
501+
_filters,
502+
Instance)
461503
{
462504
Result = await InvokeActionAsync(_actionExecutingContext),
463505
};
@@ -466,7 +508,10 @@ private async Task<ActionExecutedContext> InvokeActionFilterAsync()
466508
catch (Exception exception)
467509
{
468510
// Exceptions thrown by the action method OR filters bubble back up through ActionExcecutedContext.
469-
_actionExecutedContext = new ActionExecutedContext(_actionExecutingContext, _filters)
511+
_actionExecutedContext = new ActionExecutedContext(
512+
_actionExecutingContext,
513+
_filters,
514+
Instance)
470515
{
471516
ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception)
472517
};
@@ -478,7 +523,7 @@ private async Task InvokeAllResultFiltersAsync(IActionResult result)
478523
{
479524
_cursor.SetStage(FilterStage.ResultFilters);
480525

481-
_resultExecutingContext = new ResultExecutingContext(ActionContext, _filters, result);
526+
_resultExecutingContext = new ResultExecutingContext(ActionContext, _filters, result, Instance);
482527
await InvokeResultFilterAsync();
483528

484529
Debug.Assert(_resultExecutingContext != null);
@@ -525,7 +570,8 @@ private async Task<ResultExecutedContext> InvokeResultFilterAsync()
525570
_resultExecutedContext = new ResultExecutedContext(
526571
_resultExecutingContext,
527572
_filters,
528-
_resultExecutingContext.Result)
573+
_resultExecutingContext.Result,
574+
Instance)
529575
{
530576
Canceled = true,
531577
};
@@ -536,7 +582,8 @@ private async Task<ResultExecutedContext> InvokeResultFilterAsync()
536582
_resultExecutedContext = new ResultExecutedContext(
537583
_resultExecutingContext,
538584
_filters,
539-
_resultExecutingContext.Result)
585+
_resultExecutingContext.Result,
586+
Instance)
540587
{
541588
Canceled = true,
542589
};
@@ -552,7 +599,8 @@ private async Task<ResultExecutedContext> InvokeResultFilterAsync()
552599
_resultExecutedContext = new ResultExecutedContext(
553600
_resultExecutingContext,
554601
_filters,
555-
_resultExecutingContext.Result)
602+
_resultExecutingContext.Result,
603+
Instance)
556604
{
557605
Canceled = true,
558606
};
@@ -570,15 +618,17 @@ private async Task<ResultExecutedContext> InvokeResultFilterAsync()
570618
_resultExecutedContext = new ResultExecutedContext(
571619
_resultExecutingContext,
572620
_filters,
573-
_resultExecutingContext.Result);
621+
_resultExecutingContext.Result,
622+
Instance);
574623
}
575624
}
576625
catch (Exception exception)
577626
{
578627
_resultExecutedContext = new ResultExecutedContext(
579628
_resultExecutingContext,
580629
_filters,
581-
_resultExecutingContext.Result)
630+
_resultExecutingContext.Result,
631+
Instance)
582632
{
583633
ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception)
584634
};

src/Microsoft.AspNet.Mvc.Core/Filters/ActionExecutedContext.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,17 @@ public class ActionExecutedContext : FilterContext
1414

1515
public ActionExecutedContext(
1616
[NotNull] ActionContext actionContext,
17-
[NotNull] IList<IFilter> filters)
17+
[NotNull] IList<IFilter> filters,
18+
object controller)
1819
: base(actionContext, filters)
1920
{
21+
Controller = controller;
2022
}
2123

2224
public virtual bool Canceled { get; set; }
2325

26+
public virtual object Controller { get; }
27+
2428
public virtual Exception Exception
2529
{
2630
get

src/Microsoft.AspNet.Mvc.Core/Filters/ActionExecutingContext.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,18 @@ public class ActionExecutingContext : FilterContext
1010
public ActionExecutingContext(
1111
[NotNull] ActionContext actionContext,
1212
[NotNull] IList<IFilter> filters,
13-
[NotNull] IDictionary<string, object> actionArguments)
13+
[NotNull] IDictionary<string, object> actionArguments,
14+
object controller)
1415
: base(actionContext, filters)
1516
{
1617
ActionArguments = actionArguments;
18+
Controller = controller;
1719
}
1820

1921
public virtual IActionResult Result { get; set; }
2022

21-
public virtual IDictionary<string, object> ActionArguments { get; private set; }
23+
public virtual IDictionary<string, object> ActionArguments { get; }
24+
25+
public virtual object Controller { get; }
2226
}
2327
}

src/Microsoft.AspNet.Mvc.Core/Filters/ResultExecutedContext.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,18 @@ public class ResultExecutedContext : FilterContext
1515
public ResultExecutedContext(
1616
[NotNull] ActionContext actionContext,
1717
[NotNull] IList<IFilter> filters,
18-
[NotNull] IActionResult result)
18+
[NotNull] IActionResult result,
19+
object controller)
1920
: base(actionContext, filters)
2021
{
2122
Result = result;
23+
Controller = controller;
2224
}
2325

2426
public virtual bool Canceled { get; set; }
2527

28+
public virtual object Controller { get; }
29+
2630
public virtual Exception Exception
2731
{
2832
get

src/Microsoft.AspNet.Mvc.Core/Filters/ResultExecutingContext.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,16 @@ public class ResultExecutingContext : FilterContext
1010
public ResultExecutingContext(
1111
[NotNull] ActionContext actionContext,
1212
[NotNull] IList<IFilter> filters,
13-
[NotNull] IActionResult result)
13+
[NotNull] IActionResult result,
14+
object controller)
1415
: base(actionContext, filters)
1516
{
1617
Result = result;
18+
Controller = controller;
1719
}
1820

21+
public virtual object Controller { get; }
22+
1923
public virtual IActionResult Result { get; set; }
2024

2125
public virtual bool Cancel { get; set; }

0 commit comments

Comments
 (0)