Skip to content

Commit 55f1e2f

Browse files
fix allocation issue caused by ordering of MVC result executors for Task results (#41108)
* fix allocation issue caused by ordering of mvc result executors
1 parent 0fd9575 commit 55f1e2f

File tree

2 files changed

+51
-4
lines changed

2 files changed

+51
-4
lines changed

src/Mvc/Mvc.Core/src/Infrastructure/ActionMethodExecutor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ internal abstract class ActionMethodExecutor
1919
new SyncObjectResultExecutor(),
2020

2121
// Executors for async methods
22-
new AwaitableResultExecutor(),
2322
new TaskResultExecutor(),
23+
new AwaitableResultExecutor(),
2424
new TaskOfIActionResultExecutor(),
2525
new TaskOfActionResultExecutor(),
2626
new AwaitableObjectResultExecutor(),

src/Mvc/Mvc.Core/test/Infrastructure/ActionMethodExecutorTest.cs

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
// Licensed to the .NET Foundation under one or more agreements.
1+
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Reflection;
55
using Microsoft.Extensions.Internal;
6+
using System.Runtime.CompilerServices;
67

78
namespace Microsoft.AspNetCore.Mvc.Infrastructure;
89

@@ -21,6 +22,7 @@ public void ActionMethodExecutor_ExecutesVoidActions()
2122
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
2223

2324
// Assert
25+
Assert.Equal("VoidResultExecutor", actionMethodExecutor.GetType().Name);
2426
Assert.True(controller.Executed);
2527
Assert.IsType<EmptyResult>(valueTask.Result);
2628
}
@@ -38,6 +40,7 @@ public void ActionMethodExecutor_ExecutesActionsReturningIActionResult()
3840
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
3941

4042
// Assert
43+
Assert.Equal("SyncActionResultExecutor", actionMethodExecutor.GetType().Name);
4144
Assert.True(valueTask.IsCompleted);
4245
Assert.IsType<ContentResult>(valueTask.Result);
4346
}
@@ -55,6 +58,7 @@ public void ActionMethodExecutor_ExecutesActionsReturningSubTypeOfActionResult()
5558
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
5659

5760
// Assert
61+
Assert.Equal("SyncActionResultExecutor", actionMethodExecutor.GetType().Name);
5862
Assert.IsType<ContentResult>(valueTask.Result);
5963
}
6064

@@ -72,6 +76,8 @@ public void ActionMethodExecutor_ExecutesActionsReturningActionResultOfT()
7276

7377
// Assert
7478
var result = Assert.IsType<ObjectResult>(valueTask.Result);
79+
80+
Assert.Equal("SyncObjectResultExecutor", actionMethodExecutor.GetType().Name);
7581
Assert.NotNull(result.Value);
7682
Assert.IsType<TestModel>(result.Value);
7783
Assert.Equal(typeof(TestModel), result.DeclaredType);
@@ -91,6 +97,8 @@ public void ActionMethodExecutor_ExecutesActionsReturningModelAsModel()
9197

9298
// Assert
9399
var result = Assert.IsType<ObjectResult>(valueTask.Result);
100+
101+
Assert.Equal("SyncObjectResultExecutor", actionMethodExecutor.GetType().Name);
94102
Assert.NotNull(result.Value);
95103
Assert.IsType<TestModel>(result.Value);
96104
Assert.Equal(typeof(TestModel), result.DeclaredType);
@@ -110,6 +118,8 @@ public void ActionMethodExecutor_ExecutesActionsReturningModelAsObject()
110118

111119
// Assert
112120
var result = Assert.IsType<ObjectResult>(valueTask.Result);
121+
122+
Assert.Equal("SyncObjectResultExecutor", actionMethodExecutor.GetType().Name);
113123
Assert.NotNull(result.Value);
114124
Assert.IsType<TestModel>(result.Value);
115125
Assert.Equal(typeof(object), result.DeclaredType);
@@ -128,6 +138,7 @@ public void ActionMethodExecutor_ExecutesActionsReturningActionResultAsObject()
128138
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
129139

130140
// Assert
141+
Assert.Equal("SyncActionResultExecutor", actionMethodExecutor.GetType().Name);
131142
Assert.IsType<ContentResult>(valueTask.Result);
132143
}
133144

@@ -144,10 +155,29 @@ public void ActionMethodExecutor_ExecutesActionsReturnTask()
144155
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
145156

146157
// Assert
158+
Assert.Equal("TaskResultExecutor", actionMethodExecutor.GetType().Name);
147159
Assert.True(controller.Executed);
148160
Assert.IsType<EmptyResult>(valueTask.Result);
149161
}
150162

163+
[Fact]
164+
public void ActionMethodExecutor_ExecutesActionsReturnAwaitable()
165+
{
166+
// Arrange
167+
var mapper = new ActionResultTypeMapper();
168+
var controller = new TestController();
169+
var objectMethodExecutor = GetExecutor(nameof(TestController.ReturnsAwaitable));
170+
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
171+
172+
// Act
173+
var awaitableResult = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
174+
175+
// Assert
176+
Assert.Equal("AwaitableResultExecutor", actionMethodExecutor.GetType().Name);
177+
Assert.True(controller.Executed);
178+
Assert.IsType<EmptyResult>(awaitableResult.Result);
179+
}
180+
151181
[Fact]
152182
public void ActionMethodExecutorExecutesActionsAsynchronouslyReturningIActionResult()
153183
{
@@ -161,6 +191,7 @@ public void ActionMethodExecutorExecutesActionsAsynchronouslyReturningIActionRes
161191
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
162192

163193
// Assert
194+
Assert.Equal("TaskOfIActionResultExecutor", actionMethodExecutor.GetType().Name);
164195
Assert.IsType<StatusCodeResult>(valueTask.Result);
165196
}
166197

@@ -170,15 +201,16 @@ public async Task ActionMethodExecutor_ExecutesActionsAsynchronouslyReturningAct
170201
// Arrange
171202
var mapper = new ActionResultTypeMapper();
172203
var controller = new TestController();
173-
var objectMethodExecutor = GetExecutor(nameof(TestController.ReturnIActionResultAsync));
204+
var objectMethodExecutor = GetExecutor(nameof(TestController.ReturnActionResultAsync));
174205
var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
175206

176207
// Act
177208
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
178209

179210
// Assert
180211
await valueTask;
181-
Assert.IsType<StatusCodeResult>(valueTask.Result);
212+
Assert.Equal("TaskOfActionResultExecutor", actionMethodExecutor.GetType().Name);
213+
Assert.IsType<ViewResult>(valueTask.Result);
182214
}
183215

184216
[Fact]
@@ -195,6 +227,8 @@ public void ActionMethodExecutor_ExecutesActionsAsynchronouslyReturningModel()
195227

196228
// Assert
197229
var result = Assert.IsType<ObjectResult>(valueTask.Result);
230+
231+
Assert.Equal("AwaitableObjectResultExecutor", actionMethodExecutor.GetType().Name);
198232
Assert.NotNull(result.Value);
199233
Assert.IsType<TestModel>(result.Value);
200234
Assert.Equal(typeof(TestModel), result.DeclaredType);
@@ -214,6 +248,8 @@ public void ActionMethodExecutor_ExecutesActionsAsynchronouslyReturningModelAsOb
214248

215249
// Assert
216250
var result = Assert.IsType<ObjectResult>(valueTask.Result);
251+
252+
Assert.Equal("AwaitableObjectResultExecutor", actionMethodExecutor.GetType().Name);
217253
Assert.NotNull(result.Value);
218254
Assert.IsType<TestModel>(result.Value);
219255
Assert.Equal(typeof(object), result.DeclaredType);
@@ -232,6 +268,7 @@ public void ActionMethodExecutor_ExecutesActionsAsynchronouslyReturningIActionRe
232268
var valueTask = actionMethodExecutor.Execute(mapper, objectMethodExecutor, controller, Array.Empty<object>());
233269

234270
// Assert
271+
Assert.Equal("AwaitableObjectResultExecutor", actionMethodExecutor.GetType().Name);
235272
Assert.IsType<OkResult>(valueTask.Result);
236273
}
237274

@@ -249,6 +286,8 @@ public void ActionMethodExecutor_ExecutesActionsAsynchronouslyReturningActionRes
249286

250287
// Assert
251288
var result = Assert.IsType<ObjectResult>(valueTask.Result);
289+
290+
Assert.Equal("AwaitableObjectResultExecutor", actionMethodExecutor.GetType().Name);
252291
Assert.NotNull(result.Value);
253292
Assert.IsType<TestModel>(result.Value);
254293
Assert.Equal(typeof(TestModel), result.DeclaredType);
@@ -304,8 +343,16 @@ public Task ReturnsTask()
304343
return Task.CompletedTask;
305344
}
306345

346+
public YieldAwaitable ReturnsAwaitable()
347+
{
348+
Executed = true;
349+
return Task.Yield();
350+
}
351+
307352
public Task<IActionResult> ReturnIActionResultAsync() => Task.FromResult((IActionResult)new StatusCodeResult(201));
308353

354+
public Task<ViewResult> ReturnActionResultAsync() => Task.FromResult(new ViewResult { StatusCode = 200});
355+
309356
public Task<StatusCodeResult> ReturnsIActionResultSubTypeAsync() => Task.FromResult(new StatusCodeResult(200));
310357

311358
public Task<TestModel> ReturnsModelAsModelAsync() => Task.FromResult(new TestModel());

0 commit comments

Comments
 (0)