Как добиться динамического контроллера и метода действия в ASP.NET MVC? - PullRequest
25 голосов
/ 22 июля 2010

В Asp.net MVC структура URL выглядит следующим образом:

http://example.com/{controller}/{action}/{id}

Для каждого «контроллера», скажем, http://example.com/blog,, существует BlogController.

Но моя часть {controller} URL-адреса не определена заранее, но она динамически определяется во время выполнения, как мне создать «динамический контроллер», который отображает что-либо на тот же контроллер, который затем основывается на значении иопределяет, что делать?

То же самое с {действием}, если часть {действия} моего URL также является динамической, есть ли способ запрограммировать этот сценарий?

Ответы [ 3 ]

25 голосов
/ 22 июля 2010

Абсолютно!Вам нужно переопределить DefaultControllerFactory, чтобы найти пользовательский контроллер, если он не существует.Затем вам нужно написать IActionInvoker для обработки динамических имен действий.

Ваша фабрика контроллеров будет выглядеть примерно так:

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

Тогда ваш вызывающий действия будет выглядеть так:

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

Вы можете увидеть намного больше этого кода здесь .Это старая первая попытка черновика, сделанная мной и коллегой по написанию полностью динамического конвейера MVC.Вы можете использовать его в качестве справочного материала и копировать то, что хотите.

Редактировать

Я подумал, что мне следует добавить некоторые сведения о том, что делает этот код.Мы пытались динамически построить слой MVC вокруг модели предметной области.Поэтому, если ваш домен содержит класс Product, вы можете перейти к products\alls, чтобы увидеть список всех продуктов.Если вы хотите добавить товар, перейдите на product\add.Вы можете перейти на product\edit\1 для редактирования продукта.Мы даже попробовали такие вещи, как позволить вам редактировать свойства объекта.Так что product\editprice\1?value=42 установит свойство цены продукта # 1 равным 42. (Мои пути могут быть немного неправильными, я не могу вспомнить точный синтаксис.) Надеюсь, это поможет!

7 голосов
/ 22 июля 2010

После небольшого размышления вам может быть немного проще справиться с именами динамических действий, чем в моем другом ответе. Вам все еще нужно будет переопределить фабрику контроллера по умолчанию. Я думаю, что вы могли бы определить свой маршрут как:

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

Тогда на вашем стандартном / динамическом контроллере у вас будет

public ActionResult ProcessCommand(string command, int id)
{
   switch(command)
   {
      // whatever.
   }
}
1 голос
/ 22 июля 2010

Вам нужно написать свой IControllerFactory (или, возможно, производный от DefaultControllerFactory), а затем зарегистрировать его как ControllerBuilder.

...