29

Asp.net MVCでは、URL構造は次のようになります

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

http://example.com/blogなどの「コントローラー」ごとに、BlogControllerがあります。

しかし、URLの{controller}部分は事前に決定されていませんが、実行時に動的に決定されます。同じコントローラーに何かをマップし、値に基づいて何を決定するかを決定する「動的コントローラー」を作成するにはどうすればよいですか。する?

{action}についても同じですが、URLの{action}部分も動的である場合、このシナリオをプログラムする方法はありますか?

4

4 に答える 4

27

絶対!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に設定します(私のパスは少しずれている可能性があり、正確な構文はもう思い出せません)。これがお役に立てば幸いです。

于 2010-07-22T05:11:20.187 に答える
8

もう少し考えてみると、他の答えよりも動的アクション名を処理するための少し簡単な方法があるかもしれません。デフォルトのコントローラーファクトリをオーバーライドする必要があります。次のようにルートを定義できると思います。

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

次に、デフォルト/ダイナミックコントローラーで

public ActionResult ProcessCommand(string command, int id)
{
   switch(command)
   {
      // whatever.
   }
}
于 2010-07-22T17:34:18.803 に答える
1

あなたはあなた自身を書くIControllerFactory(あるいはおそらくから派生するDefaultControllerFactory)必要があり、それからそれをに登録する必要がありますControllerBuilder

于 2010-07-21T22:08:22.940 に答える
0

Iamは.Coreで作業していますが、MVCバージョンをすべて共有します。その後、コアバージョンを共有します。

                case OwnerType.DynamicPage:
                    var dp = mediator.Handle(new Domain.DynamicPages.DynamicPageDtoQuery { ShopId = ShopId, SeoId = seoSearchDto.Id }.AsSingle());
                    if (dp != null)
                    {
                        return GetDynamicPage(dp.Id);
                    }
                    break;

//いくつかのコード

    private ActionResult GetDynamicPage(int id)
    {
        var routeObj = new
        {
            action = "Detail",
            controller = "DynamicPage",
            id = id
        };

        var bController = DependencyResolver.Current.GetService<DynamicPageController>();
        SetControllerContext(bController, routeObj);
        return bController.Detail(id);
    }

// と

private void SetControllerContext(ControllerBase controller, object routeObj)
{
    RouteValueDictionary routeValues = new RouteValueDictionary(routeObj);

    var vpd = RouteTable.Routes["Default"].GetVirtualPath(this.ControllerContext.RequestContext, routeValues);



    RouteData routeData = new RouteData();

    foreach (KeyValuePair<string, object> kvp in routeValues)
    {
        routeData.Values.Add(kvp.Key, kvp.Value);
    }

    foreach (KeyValuePair<string, object> kvp in vpd.DataTokens)
    {
        routeData.DataTokens.Add(kvp.Key, kvp.Value);
    }


    routeData.Route = vpd.Route;
    if (routeData.RouteHandler == null)
        routeData.RouteHandler = new MvcRouteHandler();


    controller.ControllerContext = new ControllerContext(this.ControllerContext.HttpContext, routeData, controller);
}
于 2021-03-02T20:15:17.660 に答える