File: Infrastructure\ControllerActionInvokerCache.cs
Web Access
Project: src\src\Mvc\Mvc.Core\src\Microsoft.AspNetCore.Mvc.Core.csproj (Microsoft.AspNetCore.Mvc.Core)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#nullable enable
 
using System.Linq;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Options;
 
namespace Microsoft.AspNetCore.Mvc.Infrastructure;
 
internal sealed class ControllerActionInvokerCache
{
    private readonly ParameterBinder _parameterBinder;
    private readonly IModelBinderFactory _modelBinderFactory;
    private readonly IModelMetadataProvider _modelMetadataProvider;
    private readonly IFilterProvider[] _filterProviders;
    private readonly IControllerFactoryProvider _controllerFactoryProvider;
    private readonly MvcOptions _mvcOptions;
 
    public ControllerActionInvokerCache(
        ParameterBinder parameterBinder,
        IModelBinderFactory modelBinderFactory,
        IModelMetadataProvider modelMetadataProvider,
        IEnumerable<IFilterProvider> filterProviders,
        IControllerFactoryProvider factoryProvider,
        IOptions<MvcOptions> mvcOptions)
    {
        _parameterBinder = parameterBinder;
        _modelBinderFactory = modelBinderFactory;
        _modelMetadataProvider = modelMetadataProvider;
        _filterProviders = filterProviders.OrderBy(item => item.Order).ToArray();
        _controllerFactoryProvider = factoryProvider;
        _mvcOptions = mvcOptions.Value;
    }
 
    public (ControllerActionInvokerCacheEntry cacheEntry, IFilterMetadata[] filters) GetCachedResult(ControllerContext controllerContext)
    {
        var actionDescriptor = controllerContext.ActionDescriptor;
 
        IFilterMetadata[] filters;
 
        var cacheEntry = actionDescriptor.CacheEntry;
 
        // We don't care about thread safety here
        if (cacheEntry is null)
        {
            var filterFactoryResult = FilterFactory.GetAllFilters(_filterProviders, controllerContext);
            filters = filterFactoryResult.Filters;
 
            var parameterDefaultValues = ParameterDefaultValues
                .GetParameterDefaultValues(actionDescriptor.MethodInfo);
 
            var objectMethodExecutor = ObjectMethodExecutor.Create(
                actionDescriptor.MethodInfo,
                actionDescriptor.ControllerTypeInfo,
                parameterDefaultValues);
 
            var controllerFactory = _controllerFactoryProvider.CreateControllerFactory(actionDescriptor);
            var controllerReleaser = _controllerFactoryProvider.CreateAsyncControllerReleaser(actionDescriptor);
            var propertyBinderFactory = ControllerBinderDelegateProvider.CreateBinderDelegate(
                _parameterBinder,
                _modelBinderFactory,
                _modelMetadataProvider,
                actionDescriptor,
                _mvcOptions);
 
            var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
            var filterExecutor = actionDescriptor.FilterDelegate is not null
                ? ActionMethodExecutor.GetFilterExecutor(actionDescriptor)
                : null;
 
            cacheEntry = new ControllerActionInvokerCacheEntry(
                filterFactoryResult.CacheableFilters,
                controllerFactory,
                controllerReleaser,
                propertyBinderFactory,
                objectMethodExecutor,
                filterExecutor ?? actionMethodExecutor,
                actionMethodExecutor);
 
            actionDescriptor.CacheEntry = cacheEntry;
        }
        else
        {
            // Filter instances from statically defined filter descriptors + from filter providers
            filters = FilterFactory.CreateUncachedFilters(_filterProviders, controllerContext, cacheEntry.CachedFilters);
        }
 
        return (cacheEntry, filters);
    }
}