24

En Asp.net MVC la estructura url va como¿Cómo lograr un controlador dinámico y un método de acción en ASP.NET MVC?

http://example.com/ {controlador}/{acción}/{id}

Para cada "controlador", dicen http://example.com/blog, hay una BlogController.

Pero mi porción de {controlador} de la url no se decide de antemano, pero se determina dinámicamente en tiempo de ejecución, ¿cómo creo un "controlador dinámico" que correlaciona cualquier cosa con el mismo controlador que luego basado en el valor y determina qué hacer?

Lo mismo con {acción}, si la porción de {acción} de mi url también es dinámica, ¿hay alguna manera de programar este escenario?

Respuesta

23

¡Absolutamente! Tendrá que anular DefaultControllerFactory para encontrar un controlador personalizado si no existe uno. Luego deberá escribir un IActionInvoker para manejar nombres de acciones dinámicas.

Su fábrica de controlador será algo como:

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; 
    } 
} 

Luego, su invocador acción sería como:

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(); 
    } 
} 

Puede ver mucho más de este código here. Es un viejo intento de borrador primero por mí y un compañero de trabajo en la escritura de una tubería de MVC completamente dinámica. Eres libre de usarlo como referencia y copiar lo que quieras.

Editar

que pensé que debería incluir algunos antecedentes sobre lo que hace que el código. Intentamos construir dinámicamente la capa MVC alrededor de un modelo de dominio. Entonces, si su dominio contiene una clase de Producto, puede navegar al products\alls para ver una lista de todos los productos. Si quisiera agregar un producto, navegaría al product\add. Puede ir al product\edit\1 para editar un producto. Incluso probamos cosas como permitirle editar propiedades en una entidad. Por lo tanto, product\editprice\1?value=42 establecería la propiedad de precio del producto n. ° 1 a 42. (Es posible que mis rutas estén un poco desajustadas, ya no puedo recordar la sintaxis exacta). ¡Espero que esto ayude!

+0

Hola @Ryan. He intentado implementar DynamicControllerFactory y DynamicActionInvoker desde https://github.com/ryanohs/DynamicServices. Durante el tiempo de ejecución me pidieron el constructor predeterminado de DynamicControllerFactory 1st. Luego agregué uno con implementación vacía. Pero el constructor de parametrización nunca se invoca y el valor _locater nunca se establece. Por lo tanto, al invocar GetControllerInstance, actionInvoker no se configura debido a un _locator nulo. donde he hecho mal¿Necesito dar el valor predeterminado para _locator, o está configurado durante el tiempo de ejecución desde otro lugar? – mesimplybj

+1

¿alguien puede elaborar el escenario cuando se requiera un controlador dinámico y un invocador de acción dinámica? – Thomas

1

Necesita escribir su propio IControllerFactory (o tal vez derivar de DefaultControllerFactory) y luego registrarlo con ControllerBuilder.

7

Después de un poco más de reflexión, puede haber una manera más simple de manejar los nombres de las acciones dinámicas que mi otra respuesta. Aún tendrá que anular la fábrica predeterminada del controlador. Creo que se podría definir su ruta como:

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

A continuación, en el controlador por defecto/dinámico que tendría

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

¿qué tal el controlador dinámico? –

Cuestiones relacionadas