ASP.NET MVC: получить все контроллеры

Можно ли получить все контроллеры для ControllerFactory?
То, что я хочу сделать, это получить список всех типов контроллеров в приложении, но последовательно.

Таким образом, все контроллеры, которые я получаю, являются теми же, что и разрешение по умолчанию для запроса.

(Фактическая задача - найти все методы действий, которые имеют данный атрибут).

Ответы

Ответ 1

Вы можете использовать отражение для перечисления всех классов в сборке и фильтровать только классы, наследуемые от класса Controller.

Лучшей ссылкой является исходный код asp.net mvc. Взгляните на реализацию ControllerTypeCache и класс ActionMethodSelector. ControllerTypeCache показывает, как получить все классы контроллеров.

       internal static bool IsControllerType(Type t) {
            return
                t != null &&
                t.IsPublic &&
                t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
                !t.IsAbstract &&
                typeof(IController).IsAssignableFrom(t);
        }

 public void EnsureInitialized(IBuildManager buildManager) {
            if (_cache == null) {
                lock (_lockObj) {
                    if (_cache == null) {
                        List<Type> controllerTypes = GetAllControllerTypes(buildManager);
                        var groupedByName = controllerTypes.GroupBy(
                            t => t.Name.Substring(0, t.Name.Length - "Controller".Length),
                            StringComparer.OrdinalIgnoreCase);
                        _cache = groupedByName.ToDictionary(
                            g => g.Key,
                            g => g.ToLookup(t => t.Namespace ?? String.Empty, StringComparer.OrdinalIgnoreCase),
                            StringComparer.OrdinalIgnoreCase);
                    }
                }
            }
        }

И ActionMethodSelector показывает, как проверить, имеет ли метод желаемый атрибут.

private static List<MethodInfo> RunSelectionFilters(ControllerContext controllerContext, List<MethodInfo> methodInfos) {
            // remove all methods which are opting out of this request
            // to opt out, at least one attribute defined on the method must return false

            List<MethodInfo> matchesWithSelectionAttributes = new List<MethodInfo>();
            List<MethodInfo> matchesWithoutSelectionAttributes = new List<MethodInfo>();

            foreach (MethodInfo methodInfo in methodInfos) {
                ActionMethodSelectorAttribute[] attrs = (ActionMethodSelectorAttribute[])methodInfo.GetCustomAttributes(typeof(ActionMethodSelectorAttribute), true /* inherit */);
                if (attrs.Length == 0) {
                    matchesWithoutSelectionAttributes.Add(methodInfo);
                }
                else if (attrs.All(attr => attr.IsValidForRequest(controllerContext, methodInfo))) {
                    matchesWithSelectionAttributes.Add(methodInfo);
                }
            }

            // if a matching action method had a selection attribute, consider it more specific than a matching action method
            // without a selection attribute
            return (matchesWithSelectionAttributes.Count > 0) ? matchesWithSelectionAttributes : matchesWithoutSelectionAttributes;
        }

Ответ 2

Я не думаю, что можно дать простой ответ на этот вопрос, потому что это зависит от множества других вещей, включая реализацию IControllerFactory.

Например, если у вас есть полностью выполненная на заказ реализация IControllerFactory, все ставки неактивны, потому что он может использовать любой механизм для создания экземпляров контроллера.

Однако DefaultControllerFactory ищет соответствующий тип контроллера во всех сборках, определенных в RouteCollection (настроен в global.asax).

В этом случае вы можете пропустить все сборки, связанные с RouteCollection, и искать контроллеры в каждом.

Поиск контроллеров в данной сборке относительно прост:

var controllerTypes = from t in asm.GetExportedTypes()
                      where typeof(IController).IsAssignableFrom(t)
                      select t;

где asm является экземпляром сборки.