Skip to content

Commit f290810

Browse files
authored
Store cache entry on the ControllerActionDescriptor (#27763)
- Remove dictionary lookup and version check per request
1 parent 2366890 commit f290810

File tree

3 files changed

+11
-46
lines changed

3 files changed

+11
-46
lines changed

src/Mvc/Mvc.Core/src/Controllers/ControllerActionDescriptor.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Globalization;
77
using System.Reflection;
88
using Microsoft.AspNetCore.Mvc.Abstractions;
9+
using Microsoft.AspNetCore.Mvc.Infrastructure;
910
using Microsoft.Extensions.Internal;
1011

1112
namespace Microsoft.AspNetCore.Mvc.Controllers
@@ -36,6 +37,9 @@ public class ControllerActionDescriptor : ActionDescriptor
3637
/// </summary>
3738
public TypeInfo ControllerTypeInfo { get; set; }
3839

40+
// Cache entry so we can avoid an external cache
41+
internal ControllerActionInvokerCacheEntry CacheEntry { get; set; }
42+
3943
/// <inheritdoc />
4044
public override string DisplayName
4145
{
Lines changed: 7 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4-
using System.Collections.Concurrent;
54
using System.Collections.Generic;
65
using System.Linq;
7-
using Microsoft.AspNetCore.Mvc.Abstractions;
86
using Microsoft.AspNetCore.Mvc.Controllers;
97
using Microsoft.AspNetCore.Mvc.Filters;
10-
using Microsoft.AspNetCore.Mvc.Infrastructure;
118
using Microsoft.AspNetCore.Mvc.ModelBinding;
129
using Microsoft.Extensions.Internal;
1310
using Microsoft.Extensions.Options;
@@ -16,25 +13,21 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
1613
{
1714
internal class ControllerActionInvokerCache
1815
{
19-
private readonly IActionDescriptorCollectionProvider _collectionProvider;
2016
private readonly ParameterBinder _parameterBinder;
2117
private readonly IModelBinderFactory _modelBinderFactory;
2218
private readonly IModelMetadataProvider _modelMetadataProvider;
2319
private readonly IFilterProvider[] _filterProviders;
2420
private readonly IControllerFactoryProvider _controllerFactoryProvider;
2521
private readonly MvcOptions _mvcOptions;
26-
private volatile InnerCache _currentCache;
2722

2823
public ControllerActionInvokerCache(
29-
IActionDescriptorCollectionProvider collectionProvider,
3024
ParameterBinder parameterBinder,
3125
IModelBinderFactory modelBinderFactory,
3226
IModelMetadataProvider modelMetadataProvider,
3327
IEnumerable<IFilterProvider> filterProviders,
3428
IControllerFactoryProvider factoryProvider,
3529
IOptions<MvcOptions> mvcOptions)
3630
{
37-
_collectionProvider = collectionProvider;
3831
_parameterBinder = parameterBinder;
3932
_modelBinderFactory = modelBinderFactory;
4033
_modelMetadataProvider = modelMetadataProvider;
@@ -43,30 +36,16 @@ public ControllerActionInvokerCache(
4336
_mvcOptions = mvcOptions.Value;
4437
}
4538

46-
private InnerCache CurrentCache
47-
{
48-
get
49-
{
50-
var current = _currentCache;
51-
var actionDescriptors = _collectionProvider.ActionDescriptors;
52-
53-
if (current == null || current.Version != actionDescriptors.Version)
54-
{
55-
current = new InnerCache(actionDescriptors.Version);
56-
_currentCache = current;
57-
}
58-
59-
return current;
60-
}
61-
}
62-
6339
public (ControllerActionInvokerCacheEntry cacheEntry, IFilterMetadata[] filters) GetCachedResult(ControllerContext controllerContext)
6440
{
65-
var cache = CurrentCache;
6641
var actionDescriptor = controllerContext.ActionDescriptor;
6742

6843
IFilterMetadata[] filters;
69-
if (!cache.Entries.TryGetValue(actionDescriptor, out var cacheEntry))
44+
45+
var cacheEntry = actionDescriptor.CacheEntry;
46+
47+
// We don't care about thread safety here
48+
if (cacheEntry is null)
7049
{
7150
var filterFactoryResult = FilterFactory.GetAllFilters(_filterProviders, controllerContext);
7251
filters = filterFactoryResult.Filters;
@@ -97,7 +76,8 @@ private InnerCache CurrentCache
9776
propertyBinderFactory,
9877
objectMethodExecutor,
9978
actionMethodExecutor);
100-
cacheEntry = cache.Entries.GetOrAdd(actionDescriptor, cacheEntry);
79+
80+
actionDescriptor.CacheEntry = cacheEntry;
10181
}
10282
else
10383
{
@@ -107,18 +87,5 @@ private InnerCache CurrentCache
10787

10888
return (cacheEntry, filters);
10989
}
110-
111-
private class InnerCache
112-
{
113-
public InnerCache(int version)
114-
{
115-
Version = version;
116-
}
117-
118-
public ConcurrentDictionary<ActionDescriptor, ControllerActionInvokerCacheEntry> Entries { get; } =
119-
new ConcurrentDictionary<ActionDescriptor, ControllerActionInvokerCacheEntry>();
120-
121-
public int Version { get; }
122-
}
12390
}
12491
}

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ public void GetControllerActionMethodExecutor_CachesFilters()
2929
new FilterDescriptor(filter, FilterScope.Action)
3030
});
3131
var controllerActionInvokerCache = CreateControllerActionInvokerCache(
32-
controllerContext,
3332
new[] { new DefaultFilterProvider() });
3433

3534
// Act
@@ -50,7 +49,6 @@ public void GetControllerActionMethodExecutor_CachesEntry()
5049
new FilterDescriptor(filter, FilterScope.Action)
5150
});
5251
var controllerActionInvokerCache = CreateControllerActionInvokerCache(
53-
controllerContext,
5452
new[] { new DefaultFilterProvider() });
5553

5654
// Act
@@ -93,17 +91,13 @@ public CustomActionDescriptorCollectionProvider(ControllerActionDescriptor[] act
9391
}
9492

9593
private static ControllerActionInvokerCache CreateControllerActionInvokerCache(
96-
ControllerContext controllerContext,
9794
IFilterProvider[] filterProviders)
9895
{
99-
var descriptorProvider = new CustomActionDescriptorCollectionProvider(
100-
new[] { controllerContext.ActionDescriptor });
10196
var modelMetadataProvider = new EmptyModelMetadataProvider();
10297
var modelBinderFactory = TestModelBinderFactory.CreateDefault();
10398
var mvcOptions = Options.Create(new MvcOptions());
10499

105100
return new ControllerActionInvokerCache(
106-
descriptorProvider,
107101
new ParameterBinder(
108102
modelMetadataProvider,
109103
modelBinderFactory,

0 commit comments

Comments
 (0)