首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

ASP.NET MVC 核心解析:ControllerDescriptor 與 Action 選擇機制

     編輯:ASP.NET 基礎

前言:從 ActionInvoker 切入

ASP.NET MVC 的請求處理核心在於 ControllerActionInvoker,其 InvokeAction 方法協調了授權、模型繫結、動作執行、結果執行等完整管線。本文聚焦於 ControllerDescriptor 與 ActionDescriptor 如何被建立、快取,以及動作選擇的核心邏輯。

ActionInvoker 與 ControllerDescriptor 的關係

public virtual bool InvokeAction(ControllerContext controllerContext, string actionName)
{
    // 1. 取得 ControllerDescriptor(控制器描述器)
    ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);

    // 2. 尋找對應的 ActionDescriptor
    ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);
    // ... 後續管線略
}

protected virtual ControllerDescriptor GetControllerDescriptor(ControllerContext controllerContext)
{
    Type controllerType = controllerContext.Controller.GetType();
    // 從快取取得或建立 ReflectedControllerDescriptor
    return DescriptorCache.GetDescriptor(controllerType, 
        () => new ReflectedControllerDescriptor(controllerType));
}

快取機制:DescriptorCache 與 ControllerDescriptorCache

MVC 大量使用快取避免重複反射。ControllerDescriptorCache 繼承 ReaderWriterCache<TKey, TValue>,內部以 Dictionary<Type, ControllerDescriptor> 儲存。

internal sealed class ControllerDescriptorCache : ReaderWriterCache<Type, ControllerDescriptor>
{
    public ControllerDescriptor GetDescriptor(Type controllerType, Func<ControllerDescriptor> creator)
    {
        return FetchOrCreateItem(controllerType, creator);
    }
}

FetchOrCreateItem 邏輯:若快取命中直接回傳,否則呼叫 creator 建立實例並存入快取。

ReflectedControllerDescriptor 建構與 ActionMethodSelector

public ReflectedControllerDescriptor(Type controllerType)
{
    _controllerType = controllerType;
    _selector = new ActionMethodSelector(_controllerType); // 關鍵:動作方法選擇器
}

public ActionMethodSelector(Type controllerType)
{
    ControllerType = controllerType;
    PopulateLookupTables();
}

private void PopulateLookupTables()
{
    // 取得所有 public instance 方法
    MethodInfo[] allMethods = ControllerType.GetMethods(BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public);
    // 過濾有效動作方法
    MethodInfo[] actionMethods = Array.FindAll(allMethods, IsValidActionMethod);
    // 分離具別名與無別名方法
    AliasedMethods = Array.FindAll(actionMethods, IsMethodDecoratedWithAliasingAttribute);
    NonAliasedMethods = actionMethods.Except(AliasedMethods)
        .ToLookup(m => m.Name, StringComparer.OrdinalIgnoreCase);
}

ActionMethodSelector 在建構時一次性掃描控制器所有方法,建立查找表,極大提升後續 FindAction 效能。

ActionDescriptor 的尋找與建立

protected virtual ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
{
    return controllerDescriptor.FindAction(controllerContext, actionName);
}

// ReflectedControllerDescriptor.FindAction
public override ActionDescriptor FindAction(ControllerContext controllerContext, string actionName)
{
    MethodInfo matched = _selector.FindActionMethod(controllerContext, actionName);
    return matched != null ? new ReflectedActionDescriptor(matched, actionName, this) : null;
}

動作方法選擇核心邏輯:FindActionMethod

public MethodInfo FindActionMethod(ControllerContext controllerContext, string actionName)
{
    // 1. 收集具別名匹配的方法
    List<MethodInfo> methods = GetMatchingAliasedMethods(controllerContext, actionName);
    // 2. 加入無別名同名方法
    methods.AddRange(NonAliasedMethods[actionName]);

    // 3. 執行選擇過濾器
    List<MethodInfo> finalMethods = RunSelectionFilters(controllerContext, methods);

    // 4. 判斷結果
    switch (finalMethods.Count)
    {
        case 0: return null;
        case 1: return finalMethods[0];
        default: throw CreateAmbiguousMatchException(finalMethods, actionName);
    }
}

屬性快取:ReflectedAttributeCache

為避免重複 GetCustomAttributes,MVC 引入 ReflectedAttributeCache,以 ConcurrentDictionary 快取各類屬性:

  • ActionMethodSelectorAttribute(如 HttpGet, ActionName, NonAction)
  • ActionNameSelectorAttribute
  • FilterAttribute(動作、授權、結果、例外過濾器)
private static ReadOnlyCollection<TAttribute> GetAttributes<TMemberInfo, TAttribute>(
    ConcurrentDictionary<TMemberInfo, ReadOnlyCollection<TAttribute>> lookup,
    TMemberInfo memberInfo)
    where TAttribute : Attribute
    where TMemberInfo : MemberInfo
{
    return lookup.GetOrAdd(memberInfo, 
        mi => new ReadOnlyCollection<TAttribute>(
            (TAttribute[])memberInfo.GetCustomAttributes(typeof(TAttribute), true)));
}

ReflectedActionDescriptor 驗證邏輯

建立時會驗證方法是否可呼叫,拒絕條件:

  1. 靜態方法
  2. 非 ControllerBase 實例方法
  3. 含泛型參數(如 Index<T>()),但允許泛型集合參數(如 Index(List<string>))
  4. 參數含 ref / out

總結:MVC 多層快取架構

  1. ControllerType 快取:啟動時掃描組件所有 Controller,建立 Type 快取。
  2. DescriptorCache:ControllerType → ReflectedControllerDescriptor。
  3. ReflectedControllerDescriptor:建構時掃描所有 Action 方法,建立 ActionMethodSelector 查找表。
  4. ReflectedAttributeCache:快取每個 MethodInfo 的各類屬性集合。

這層層快取設計,確保 MVC 在高併發下仍能維持優異的路由解析效能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved