120 lines
4.1 KiB
C#
120 lines
4.1 KiB
C#
// Copyright (c) .NET Foundation. All rights reserved.
|
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
|
using Microsoft.AspNetCore.Mvc.Filters;
|
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
|
using Microsoft.Extensions.Internal;
|
|
|
|
namespace Microsoft.AspNetCore.Mvc.Internal
|
|
{
|
|
public class ControllerActionInvokerCache
|
|
{
|
|
private readonly IActionDescriptorCollectionProvider _collectionProvider;
|
|
private readonly IFilterProvider[] _filterProviders;
|
|
|
|
private volatile InnerCache _currentCache;
|
|
|
|
public ControllerActionInvokerCache(
|
|
IActionDescriptorCollectionProvider collectionProvider,
|
|
IEnumerable<IFilterProvider> filterProviders)
|
|
{
|
|
_collectionProvider = collectionProvider;
|
|
_filterProviders = filterProviders.OrderBy(item => item.Order).ToArray();
|
|
}
|
|
|
|
private InnerCache CurrentCache
|
|
{
|
|
get
|
|
{
|
|
var current = _currentCache;
|
|
var actionDescriptors = _collectionProvider.ActionDescriptors;
|
|
|
|
if (current == null || current.Version != actionDescriptors.Version)
|
|
{
|
|
current = new InnerCache(actionDescriptors.Version);
|
|
_currentCache = current;
|
|
}
|
|
|
|
return current;
|
|
}
|
|
}
|
|
|
|
public ControllerActionInvokerState GetState(ControllerContext controllerContext)
|
|
{
|
|
var cache = CurrentCache;
|
|
var actionDescriptor = controllerContext.ActionDescriptor;
|
|
|
|
IFilterMetadata[] filters;
|
|
Entry cacheEntry;
|
|
if (!cache.Entries.TryGetValue(actionDescriptor, out cacheEntry))
|
|
{
|
|
var filterFactoryResult = FilterFactory.GetAllFilters(_filterProviders, controllerContext);
|
|
filters = filterFactoryResult.Filters;
|
|
|
|
var parameterDefaultValues = ParameterDefaultValues
|
|
.GetParameterDefaultValues(actionDescriptor.MethodInfo);
|
|
|
|
var executor = ObjectMethodExecutor.Create(
|
|
actionDescriptor.MethodInfo,
|
|
actionDescriptor.ControllerTypeInfo,
|
|
parameterDefaultValues);
|
|
|
|
cacheEntry = new Entry(filterFactoryResult.CacheableFilters, executor);
|
|
cacheEntry = cache.Entries.GetOrAdd(actionDescriptor, cacheEntry);
|
|
}
|
|
else
|
|
{
|
|
// Filter instances from statically defined filter descriptors + from filter providers
|
|
filters = FilterFactory.CreateUncachedFilters(_filterProviders, controllerContext, cacheEntry.FilterItems);
|
|
}
|
|
|
|
return new ControllerActionInvokerState(filters, cacheEntry.ActionMethodExecutor);
|
|
}
|
|
|
|
private class InnerCache
|
|
{
|
|
public InnerCache(int version)
|
|
{
|
|
Version = version;
|
|
}
|
|
|
|
public ConcurrentDictionary<ActionDescriptor, Entry> Entries { get; } =
|
|
new ConcurrentDictionary<ActionDescriptor, Entry>();
|
|
|
|
public int Version { get; }
|
|
}
|
|
|
|
private struct Entry
|
|
{
|
|
public Entry(FilterItem[] items, ObjectMethodExecutor executor)
|
|
{
|
|
FilterItems = items;
|
|
ActionMethodExecutor = executor;
|
|
}
|
|
|
|
public FilterItem[] FilterItems { get; }
|
|
|
|
public ObjectMethodExecutor ActionMethodExecutor { get; }
|
|
}
|
|
|
|
public struct ControllerActionInvokerState
|
|
{
|
|
internal ControllerActionInvokerState(
|
|
IFilterMetadata[] filters,
|
|
ObjectMethodExecutor actionMethodExecutor)
|
|
{
|
|
Filters = filters;
|
|
ActionMethodExecutor = actionMethodExecutor;
|
|
}
|
|
|
|
public IFilterMetadata[] Filters { get; }
|
|
|
|
internal ObjectMethodExecutor ActionMethodExecutor { get; }
|
|
}
|
|
}
|
|
}
|