Zdarzają się sytuacje (jakie - o tym niedługo) w których przydałoby się dostać informację o tym, który kontroler zajmie się przetwarzaniem żądania. Niestety takie dane są dość ukryte we flakach MVC. Zrozumiałe jest, że taka logika jest częścią frameworka - w końcu to właśnie framework jest odpowiedzialny za utworzenie kontrolera na podstawie danych wysłanych z przeglądarki - ale dlaczego od razu chować tą logikę za jakimiś "internal"?
Standardowo, o ile nie chcemy wpinać się w proces tworzenia kontrolera, wykorzystywana jest do tego celu klasa DefaultControllerFactory z metodą CreateController. Łatwo zauważyć, że klasa ta ma także metodę GetControllerType, która mnie interesuje. Ale niestety programiści w MS, jak to mają w zwyczaju, nadali jej atrybuty protected internal.
Jak widać, trochę kodu w niej siedzi:

Można go przekleić (np z megacoolerskiego dotPeek) do swojego projektu, ale... po co?
Oszukamy MS!
Najpierw napiszemy własną fabrykę kontrolerów. Nie po to, żeby coś zmieniać, ale po to aby udostępnić wspomnianą metodę na zewnątrz:
1: public class MyControllerFactory : DefaultControllerFactory
2: {
3: /// <summary>
4: /// Opens base method for external use
5: /// </summary>
6: public Type FindControllerType(RequestContext requestContext, string controllerName)
7: {
8: return base.GetControllerType(requestContext, controllerName);
9: }
10: }
Potem powiemy MVC, że ma użyć tej a nie innej klasy do znajdowania kontrolerów (w global.asax.cs):
1: public override void Init()
2: {
3: base.Init();
4: ControllerBuilder.Current.SetControllerFactory(new MyControllerFactory());
5: }
I już.
Dodatkowo napisałem sobie taki util:
1: public static class ControllerTypeLocator
2: {
3: private static List<Type> _allControllers;
4: private static readonly object _syncRoot = new object();
5:
6: private static void DiscoverAllControllers()
7: {
8: if (_allControllers == null)
9: {
10: lock (_syncRoot)
11: {
12: if (_allControllers == null)
13: {
14: var allTypes = AppDomain.CurrentDomain.GetMyAppTypes();
15: _allControllers = allTypes
16: .Where(t => t.CanBeInstantiated() && t.IsAssignableTo<Controller>())
17: .ToList();
18: }
19: }
20: }
21: }
22:
23: public static Type FindControllerType(RequestContext requestContext, string controllerName)
24: {
25: var controllerFactory = ControllerBuilder.Current.GetControllerFactory()
26: as MyControllerFactory;
27:
28: Type controllerType = controllerFactory.FindControllerType(requestContext, controllerName);
29:
30: if (controllerType == null)
31: {
32:
33:
34: DiscoverAllControllers();
35: controllerType = _allControllers.Where(x => x.Name == controllerName + "Controller")
36: .Single();
37: }
38:
39: return controllerType;
40: }
41: }
Nie jest to na pewno jakaś super-wyrafinowana technika, ale spełnia swoje zadanie - przebija się przez MSową gardę internali.