zoukankan      html  css  js  c++  java
  • asp.net core mvc 3.1 源码分析(六)

    通过DefaultActionDescriptorCollectionProvider类,我们获取到了应用的Controller和Action相关信息

    后面分析Action是如何执行的

    通过IActionSelector找到对应的ActionDescriptor

    然后调用IActionInvokerFactory的CreateInvoker的方法创建IActionInvoker对象

    internal class ActionInvokerFactory : IActionInvokerFactory
        {
            private readonly IActionInvokerProvider[] _actionInvokerProviders;
    
            public ActionInvokerFactory(IEnumerable<IActionInvokerProvider> actionInvokerProviders)
            {
                _actionInvokerProviders = actionInvokerProviders.OrderBy(item => item.Order).ToArray();
            }
    
            public IActionInvoker CreateInvoker(ActionContext actionContext)
            {
                var context = new ActionInvokerProviderContext(actionContext);
    
                foreach (var provider in _actionInvokerProviders)
                {
                    provider.OnProvidersExecuting(context);
                }
    
                for (var i = _actionInvokerProviders.Length - 1; i >= 0; i--)
                {
                    _actionInvokerProviders[i].OnProvidersExecuted(context);
                }
    
                return context.Result;
            }
        }

    internal class ControllerActionInvokerProvider : IActionInvokerProvider
        {
            private readonly ControllerActionInvokerCache _controllerActionInvokerCache;
            private readonly IReadOnlyList<IValueProviderFactory> _valueProviderFactories;
            private readonly int _maxModelValidationErrors;
            private readonly ILogger _logger;
            private readonly DiagnosticListener _diagnosticListener;
            private readonly IActionResultTypeMapper _mapper;
            private readonly IActionContextAccessor _actionContextAccessor;
    
            public ControllerActionInvokerProvider(
                ControllerActionInvokerCache controllerActionInvokerCache,
                IOptions<MvcOptions> optionsAccessor,
                ILoggerFactory loggerFactory,
                DiagnosticListener diagnosticListener,
                IActionResultTypeMapper mapper)
                : this(controllerActionInvokerCache, optionsAccessor, loggerFactory, diagnosticListener, mapper, null)
            {
            }
    
            public ControllerActionInvokerProvider(
                ControllerActionInvokerCache controllerActionInvokerCache,
                IOptions<MvcOptions> optionsAccessor,
                ILoggerFactory loggerFactory,
                DiagnosticListener diagnosticListener,
                IActionResultTypeMapper mapper,
                IActionContextAccessor actionContextAccessor)
            {
                _controllerActionInvokerCache = controllerActionInvokerCache;
                _valueProviderFactories = optionsAccessor.Value.ValueProviderFactories.ToArray();
                _maxModelValidationErrors = optionsAccessor.Value.MaxModelValidationErrors;
                _logger = loggerFactory.CreateLogger<ControllerActionInvoker>();
                _diagnosticListener = diagnosticListener;
                _mapper = mapper;
                _actionContextAccessor = actionContextAccessor ?? ActionContextAccessor.Null;
            }
    
            public int Order => -1000;
    
            /// <inheritdoc />
            public void OnProvidersExecuting(ActionInvokerProviderContext context)
            {
                if (context == null)
                {
                    throw new ArgumentNullException(nameof(context));
                }
    
                if (context.ActionContext.ActionDescriptor is ControllerActionDescriptor)
                {
                    var controllerContext = new ControllerContext(context.ActionContext)
                    {
                        // PERF: These are rarely going to be changed, so let's go copy-on-write.
                        ValueProviderFactories = new CopyOnWriteList<IValueProviderFactory>(_valueProviderFactories)
                    };
                    controllerContext.ModelState.MaxAllowedErrors = _maxModelValidationErrors;
    
                    var (cacheEntry, filters) = _controllerActionInvokerCache.GetCachedResult(controllerContext);
    
                    var invoker = new ControllerActionInvoker(
                        _logger,
                        _diagnosticListener,
                        _actionContextAccessor,
                        _mapper,
                        controllerContext,
                        cacheEntry,
                        filters);
    
                    context.Result = invoker;
                }
            }
    
            /// <inheritdoc />
            public void OnProvidersExecuted(ActionInvokerProviderContext context)
            {
            }
        }
    internal class ControllerActionInvokerCache
        {
            private readonly IActionDescriptorCollectionProvider _collectionProvider;
            private readonly ParameterBinder _parameterBinder;
            private readonly IModelBinderFactory _modelBinderFactory;
            private readonly IModelMetadataProvider _modelMetadataProvider;
            private readonly IFilterProvider[] _filterProviders;
            private readonly IControllerFactoryProvider _controllerFactoryProvider;
            private readonly MvcOptions _mvcOptions;
            private volatile InnerCache _currentCache;
    
            public ControllerActionInvokerCache(
                IActionDescriptorCollectionProvider collectionProvider,
                ParameterBinder parameterBinder,
                IModelBinderFactory modelBinderFactory,
                IModelMetadataProvider modelMetadataProvider,
                IEnumerable<IFilterProvider> filterProviders,
                IControllerFactoryProvider factoryProvider,
                IOptions<MvcOptions> mvcOptions)
            {
                _collectionProvider = collectionProvider;
                _parameterBinder = parameterBinder;
                _modelBinderFactory = modelBinderFactory;
                _modelMetadataProvider = modelMetadataProvider;
                _filterProviders = filterProviders.OrderBy(item => item.Order).ToArray();
                _controllerFactoryProvider = factoryProvider;
                _mvcOptions = mvcOptions.Value;
            }
    
            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 (ControllerActionInvokerCacheEntry cacheEntry, IFilterMetadata[] filters) GetCachedResult(ControllerContext controllerContext)
            {
                var cache = CurrentCache;
                var actionDescriptor = controllerContext.ActionDescriptor;
    
                IFilterMetadata[] filters;
                if (!cache.Entries.TryGetValue(actionDescriptor, out var cacheEntry))
                {
                    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.CreateControllerReleaser(actionDescriptor);
                    var propertyBinderFactory = ControllerBinderDelegateProvider.CreateBinderDelegate(
                        _parameterBinder,
                        _modelBinderFactory,
                        _modelMetadataProvider,
                        actionDescriptor,
                        _mvcOptions);
    
                    var actionMethodExecutor = ActionMethodExecutor.GetExecutor(objectMethodExecutor);
    
                    cacheEntry = new ControllerActionInvokerCacheEntry(
                        filterFactoryResult.CacheableFilters,
                        controllerFactory,
                        controllerReleaser,
                        propertyBinderFactory,
                        objectMethodExecutor,
                        actionMethodExecutor);
                    cacheEntry = cache.Entries.GetOrAdd(actionDescriptor, cacheEntry);
                }
                else
                {
                    // Filter instances from statically defined filter descriptors + from filter providers
                    filters = FilterFactory.CreateUncachedFilters(_filterProviders, controllerContext, cacheEntry.CachedFilters);
                }
    
                return (cacheEntry, filters);
            }
    
            private class InnerCache
            {
                public InnerCache(int version)
                {
                    Version = version;
                }
    
                public ConcurrentDictionary<ActionDescriptor, ControllerActionInvokerCacheEntry> Entries { get; } =
                    new ConcurrentDictionary<ActionDescriptor, ControllerActionInvokerCacheEntry>();
    
                public int Version { get; }
            }
        }
    internal static class ControllerBinderDelegateProvider
        {
            public static ControllerBinderDelegate CreateBinderDelegate(
                ParameterBinder parameterBinder,
                IModelBinderFactory modelBinderFactory,
                IModelMetadataProvider modelMetadataProvider,
                ControllerActionDescriptor actionDescriptor,
                MvcOptions mvcOptions)
            {
                if (parameterBinder == null)
                {
                    throw new ArgumentNullException(nameof(parameterBinder));
                }
    
                if (modelBinderFactory == null)
                {
                    throw new ArgumentNullException(nameof(modelBinderFactory));
                }
    
                if (modelMetadataProvider == null)
                {
                    throw new ArgumentNullException(nameof(modelMetadataProvider));
                }
    
                if (actionDescriptor == null)
                {
                    throw new ArgumentNullException(nameof(actionDescriptor));
                }
    
                if (mvcOptions == null)
                {
                    throw new ArgumentNullException(nameof(mvcOptions));
                }
    
                var parameterBindingInfo = GetParameterBindingInfo(
                    modelBinderFactory,
                    modelMetadataProvider,
                    actionDescriptor,
                    mvcOptions);
                var propertyBindingInfo = GetPropertyBindingInfo(modelBinderFactory, modelMetadataProvider, actionDescriptor);
    
                if (parameterBindingInfo == null && propertyBindingInfo == null)
                {
                    return null;
                }
    
                return Bind;
    
                async Task Bind(ControllerContext controllerContext, object controller, Dictionary<string, object> arguments)
                {
                    var (success, valueProvider) = await CompositeValueProvider.TryCreateAsync(controllerContext, controllerContext.ValueProviderFactories);
                    if (!success)
                    {
                        return;
                    }
    
                    var parameters = actionDescriptor.Parameters;
    
                    for (var i = 0; i < parameters.Count; i++)
                    {
                        var parameter = parameters[i];
                        var bindingInfo = parameterBindingInfo[i];
                        var modelMetadata = bindingInfo.ModelMetadata;
    
                        if (!modelMetadata.IsBindingAllowed)
                        {
                            continue;
                        }
    
                        var result = await parameterBinder.BindModelAsync(
                            controllerContext,
                            bindingInfo.ModelBinder,
                            valueProvider,
                            parameter,
                            modelMetadata,
                            value: null);
    
                        if (result.IsModelSet)
                        {
                            arguments[parameter.Name] = result.Model;
                        }
                    }
    
                    var properties = actionDescriptor.BoundProperties;
                    for (var i = 0; i < properties.Count; i++)
                    {
                        var property = properties[i];
                        var bindingInfo = propertyBindingInfo[i];
                        var modelMetadata = bindingInfo.ModelMetadata;
    
                        if (!modelMetadata.IsBindingAllowed)
                        {
                            continue;
                        }
    
                        var result = await parameterBinder.BindModelAsync(
                           controllerContext,
                           bindingInfo.ModelBinder,
                           valueProvider,
                           property,
                           modelMetadata,
                           value: null);
    
                        if (result.IsModelSet)
                        {
                            PropertyValueSetter.SetValue(bindingInfo.ModelMetadata, controller, result.Model);
                        }
                    }
                }
            }
    
            private static BinderItem[] GetParameterBindingInfo(
                IModelBinderFactory modelBinderFactory,
                IModelMetadataProvider modelMetadataProvider,
                ControllerActionDescriptor actionDescriptor,
                MvcOptions mvcOptions)
            {
                var parameters = actionDescriptor.Parameters;
                if (parameters.Count == 0)
                {
                    return null;
                }
    
                var parameterBindingInfo = new BinderItem[parameters.Count];
                for (var i = 0; i < parameters.Count; i++)
                {
                    var parameter = parameters[i];
    
                    ModelMetadata metadata;
                    if (modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase &&
                        parameter is ControllerParameterDescriptor controllerParameterDescriptor)
                    {
                        // The default model metadata provider derives from ModelMetadataProvider
                        // and can therefore supply information about attributes applied to parameters.
                        metadata = modelMetadataProviderBase.GetMetadataForParameter(controllerParameterDescriptor.ParameterInfo);
                    }
                    else
                    {
                        // For backward compatibility, if there's a custom model metadata provider that
                        // only implements the older IModelMetadataProvider interface, access the more
                        // limited metadata information it supplies. In this scenario, validation attributes
                        // are not supported on parameters.
                        metadata = modelMetadataProvider.GetMetadataForType(parameter.ParameterType);
                    }
    
                    var binder = modelBinderFactory.CreateBinder(new ModelBinderFactoryContext
                    {
                        BindingInfo = parameter.BindingInfo,
                        Metadata = metadata,
                        CacheToken = parameter,
                    });
    
                    parameterBindingInfo[i] = new BinderItem(binder, metadata);
                }
    
                return parameterBindingInfo;
            }
    
            private static BinderItem[] GetPropertyBindingInfo(
                IModelBinderFactory modelBinderFactory,
                IModelMetadataProvider modelMetadataProvider,
                ControllerActionDescriptor actionDescriptor)
            {
                var properties = actionDescriptor.BoundProperties;
                if (properties.Count == 0)
                {
                    return null;
                }
    
                var propertyBindingInfo = new BinderItem[properties.Count];
                var controllerType = actionDescriptor.ControllerTypeInfo.AsType();
                for (var i = 0; i < properties.Count; i++)
                {
                    var property = properties[i];
                    var metadata = modelMetadataProvider.GetMetadataForProperty(controllerType, property.Name);
                    var binder = modelBinderFactory.CreateBinder(new ModelBinderFactoryContext
                    {
                        BindingInfo = property.BindingInfo,
                        Metadata = metadata,
                        CacheToken = property,
                    });
    
                    propertyBindingInfo[i] = new BinderItem(binder, metadata);
                }
    
                return propertyBindingInfo;
            }
    
            private readonly struct BinderItem
            {
                public BinderItem(IModelBinder modelBinder, ModelMetadata modelMetadata)
                {
                    ModelBinder = modelBinder;
                    ModelMetadata = modelMetadata;
                }
    
                public IModelBinder ModelBinder { get; }
    
                public ModelMetadata ModelMetadata { get; }
            }
        }

  • 相关阅读:
    第三章 p62 或运算
    p57 字符串的长度
    p53 ASCII码
    整数类型,如同时钟
    重要:原码、反码、补码...
    p42 实验溢出(上溢)
    P40 字节单位:KMGT
    p38 二、八、十六进制的对应关系
    p13 数组元素的地址
    p11 内存中的数据和地址
  • 原文地址:https://www.cnblogs.com/lanpingwang/p/12642895.html
Copyright © 2011-2022 走看看