24

w ASP.NET MVC struktura url idzie jakJak uzyskać dynamiczny kontroler i metodę działania w ASP.NET MVC?

http://example.com/ {kontroler}/{działania}/{id}

dla każdego "kontrolerem", powiedzieć http://example.com/blog, istnieje BlogController.

Ale moja część adresu URL kontrolera nie jest ustalana z wyprzedzeniem, ale jest określana dynamicznie w czasie wykonywania, jak utworzyć "kontroler dynamiczny", który mapuje wszystko do tego samego kontrolera, który następnie bazuje na wartość i określa, co robić?

To samo z {action}, jeśli część {akcja} mojego adresu URL jest również dynamiczna, czy istnieje sposób na zaprogramowanie tego scenariusza?

Odpowiedz

23

Absolutnie! Będziesz musiał zastąpić DefaultControllerFactory, aby znaleźć niestandardowy kontroler, jeśli taki nie istnieje. Następnie musisz napisać IActionInvoker, aby obsłużyć dynamiczne nazwy akcji.

Twój fabrycznie regulator będzie wyglądać następująco:

public class DynamicControllerFactory : DefaultControllerFactory 
{ 
    private readonly IServiceLocator _Locator; 

    public DynamicControllerFactory(IServiceLocator locator) 
    { 
     _Locator = locator; 
    } 

    protected override Type GetControllerType(string controllerName) 
    { 
     var controllerType = base.GetControllerType(controllerName); 
      // if a controller wasn't found with a matching name, return our dynamic controller 
     return controllerType ?? typeof (DynamicController); 
    } 

    protected override IController GetControllerInstance(Type controllerType) 
    { 
     var controller = base.GetControllerInstance(controllerType) as Controller; 

     var actionInvoker = _Locator.GetInstance<IActionInvoker>(); 
     if (actionInvoker != null) 
     { 
      controller.ActionInvoker = actionInvoker; 
     } 

     return controller; 
    } 
} 

Wtedy twój wywołujący działanie byłoby jak:

public class DynamicActionInvoker : ControllerActionInvoker 
{ 
    private readonly IServiceLocator _Locator; 

    public DynamicActionInvoker(IServiceLocator locator) 
    { 
     _Locator = locator; 
    } 

    protected override ActionDescriptor FindAction(ControllerContext controllerContext, 
                ControllerDescriptor controllerDescriptor, string actionName) 
    { 
      // try to match an existing action name first 
     var action = base.FindAction(controllerContext, controllerDescriptor, actionName); 
     if (action != null) 
     { 
      return action; 
     } 

// @ray247 The remainder of this you'd probably write on your own... 
     var actionFinders = _Locator.GetAllInstances<IFindAction>(); 
     if (actionFinders == null) 
     { 
      return null; 
     } 

     return actionFinders 
      .Select(f => f.FindAction(controllerContext, controllerDescriptor, actionName)) 
      .Where(d => d != null) 
      .FirstOrDefault(); 
    } 
} 

można zobaczyć dużo więcej tego kodu here. Jest to stara pierwsza próba przeze mnie i współpracownika na piśmie w pełni dynamiczny rurociąg MVC. Możesz go używać jako punktu odniesienia i kopiować, co chcesz.

Edit

Pomyślałem powinien zawierać jakieś tło o co ten kod robi. Próbowaliśmy dynamicznie budować warstwę MVC wokół modelu domeny. Jeśli więc twoja domena zawierała klasę produktu, możesz przejść do products\alls, aby zobaczyć listę wszystkich produktów. Jeśli chcesz dodać produkt, możesz przejść do product\add. Możesz przejść do product\edit\1, aby edytować produkt. Próbowaliśmy nawet takich rzeczy, jak zezwolenie na edycję właściwości w encji. Tak więc product\editprice\1?value=42 ustawiłaby właściwość ceny produktu nr 1 do 42. (Moje ścieżki mogą być nieco wyłączone, nie pamiętam już dokładnej składni). Mam nadzieję, że to pomoże!

+0

Witam @Ryan. Próbowałem zaimplementować Twoje DynamicControllerFactory i DynamicActionInvoker z https://github.com/ryanohs/DynamicServices. Podczas wykonywania runtime został poproszony o domyślny konstruktor DynamicControllerFactory 1st. Następnie dodałem jeden z pustą implementacją. Ale konstruktor parametryzacji nigdy nie jest wywoływany, a wartość _locater nigdy nie jest ustawiona. Tak więc podczas wywoływania akcji GetControllerInstance akcjaInvoker nie jest ustawiona z powodu pustego obiektu _locator. gdzie popełniłem błąd.Czy muszę podać wartość domyślną dla _locator, czy jest ustawiona w czasie wykonywania z innego źródła? – mesimplybj

+1

ktokolwiek może opracować scenariusz, w którym wymagany byłby dynamiczny kontroler i dynamiczny wywoływacz akcji? – Thomas

1

Musisz napisać własną wersję IControllerFactory (lub może pochodzić z DefaultControllerFactory), a następnie zarejestrować ją pod numerem ControllerBuilder.

7

Po nieco dłuższej refleksji może istnieć nieco prostszy sposób obsługi nazw dynamicznych akcji niż moja inna odpowiedź. Nadal musisz zastąpić domyślną fabrykę kontrolera. Myślę, że można określić trasy jak:

routes.MapRoute("Dynamic", "{controller}/{command}/{id}", new { action = "ProcessCommand" }); 

następnie domyślnego/dynamicznego regulatora musiałbyś

public ActionResult ProcessCommand(string command, int id) 
{ 
    switch(command) 
    { 
     // whatever. 
    } 
} 
+0

co z kontrolerem dynamicznym? –

Powiązane problemy