MVC SiteMap - 当不同节点指向同一个动作时,SiteMap.CurrentNode不会映射到正确的路径

时间:2012-09-25 12:13:50

标签: asp.net-mvc-4 mvcsitemapprovider

设定:

我正在使用ASP.NET MVC 4,使用mvcSiteMapProvider来管理我的菜单。

我有一个自定义菜单构建器,用于评估节点是否在当前分支上(即,SiteMap.CurrentNode是CurrentNode还是CurrentNode嵌套在其下)。代码包含在下面,但基本上检查每个节点的url并将其与currentnode的url进行比较,直到currentnodes“family tree”。

我的自定义菜单构建器使用CurrentBranch添加一个类,该类突出显示CurrentBranch上的菜单项。

问题:

我的自定义菜单工作正常,但我发现mvcSiteMapProvider似乎无法以一致的方式评估url的{​​{1}}:

当两个节点指向同一个操作并且仅通过操作的参数进行区分时,SiteMap.CurrentNode似乎没有使用正确的路径(它忽略了区分参数并默认为第一个映射到节点中定义的操作的路由。

问题示例:

在我有CurrentNode的应用中。

会员的Members字段可以是“未处理”,“有效”或“无效”。要更改MemberStatus,我在名为Admin的区域中有一个MemberStatus。处理是使用ProcessMemberController上的Process操作完成的。

我的mvcSiteMap有两个节点,两个节点都映射到ProcessMemberController动作。它们之间的唯一区别是Process参数(这是我的客户端的域语义),在一种情况下,其值为“已处理”,而在另一种情况下为“未处理”:

节点

alternate

路线:

这两个节点的相应路由(同样,区别它们的唯一方法是备用参数的值):

    <mvcSiteMapNode title="Process" area="Admin" controller="ProcessMembers" action="Process" alternate="Unprocessed" />
    <mvcSiteMapNode title="Change Status" area="Admin" controller="ProcessMembers" action="Process" alternate="Processed" />

什么有效:

Html.ActionLink帮助程序使用路由并生成我期望的URL:

context.MapRoute(
                    "Process_New_Members",
                    "Admin/Unprocessed/Process/{MemberId}",
                    new { controller = "ProcessMembers", 
                        action = "Process", 
                        alternate="Unprocessed", 
                        MemberId = UrlParameter.Optional }
                );


context.MapRoute(
                    "Change_Status_Old_Members",
                    "Admin/Members/Status/Change/{MemberId}",
                    new { controller = "ProcessMembers", 
                        action = "Process", 
                        alternate="Processed", 
                        MemberId = UrlParameter.Optional }
                );

在这两种情况下,输出都是正确的,正如我所料。

什么行不通:

假设我的请求涉及第二个选项,即@Html.ActionLink("Process", MVC.Admin.ProcessMembers.Process(item.MemberId, "Unprocessed") // Output (alternate="Unprocessed" and item.MemberId = 12): Admin/Unprocessed/Process/12 @Html.ActionLink("Status", MVC.Admin.ProcessMembers.Process(item.MemberId, "Processed") // Output (alternate="Processed" and item.MemberId = 23): Admin/Members/Status/Change/23 ,对应于alternate =“Processed”和MemberId为47.

调试我的静态CurrentBranch属性(见下文),我发现SiteMap.CurrentNode显示:

/Admin/Members/Status/Change/47

即,对于/ Admin / Members / Status / Change / 47的请求网址,PreviousSibling: null Provider: {MvcSiteMapProvider.DefaultSiteMapProvider} ReadOnly: false ResourceKey: "" Roles: Count = 0 RootNode: {Home} Title: "Process" Url: "/Admin/Unprocessed/Process/47" 评估为SiteMap.CurrentNode.Url。即,它正在忽略/Admin/Unprocessed/Process/47参数并使用错误的路径。

CurrentBranch静态属性:

alternate

问题:

我做错了什么?

路由由Html.ActionLlink按我的预期解释,但不会像我期望的那样被SiteMap.CurrentNode评估。换句话说,在评估我的路由时,SiteMap.CurrentNode会忽略区分备用参数。

4 个答案:

答案 0 :(得分:3)

我认为这是因为你试图从参数中获取路线。基本上,MVC只是试图GUESS你可能指的是什么路线。

正确的方法是按名称处理路线。因此,站点地图应引用路由名称而不是控制器,操作等。

答案 1 :(得分:2)

附录 - 我已经开始工作!!:

绊脚石:

当使用不同的路由指向同一个控制器操作时,一个节点有不同的节点,我遇到的最大问题是:

你必须给一个关键的不同节点!否则,网站不会出现退货!!

EG,只有路由属性本身是不够的,你必须给每个节点指向相同的动作一个UNIQUE键来区分它们:

<mvcSiteMapNode title="Edit Staff" area="Admin" controller="EditStaff" route="Admin_AdministratorDetails" action="Start" key="administrators_details" />
<mvcSiteMapNode title="Edit Staff" area="Admin" controller="EditStaff" route="Admin_StaffDetails" action="Start" key="staff_details" />

一旦我意识到这一点,它现在已经开始了。否则,一切都是模糊不清的。

注意:

在问题中,不同路由调用的操作是Process操作。我把它改成了对不同行为的召唤。但是在编辑我的对象(Solicitors)时,我无法做到这一点,因为编辑是由MVC向导完成的,就像我的自定义菜单构建器一样,我编写并且工作得非常好。它只是不可能(或者更确切地说,DRY)三次重建向导。因此,我必须让我的菜单高亮显示正确的工作,不同的路线指向同一个动作。

没有Un-DRY的蠢货。我的客户不值得。

解决方案(请参阅有关操作和路线与问题不同的说明):

Carlos Martinez的建议有效,但您需要使用Html.RouteLink而非Html.ActionLink,以及编辑后的站点地图,详细说明路线。

基本上,在您的节点中,您需要使用route属性:

<mvcSiteMapNode title="Details Active Solicitor" area="Solicitors" controller="EditSolicitor" action="Start" route="Active_Details" key="company_activeSolicitors_details"/>

然后,在您的视图中,您使用RouteLink帮助程序而不是操作链接:

@Html.RouteLink("Details", "Active_Details", new { action="Start", controller="EditSolicitor", idSolicitor = item.SolicitorId, returnUrl = Request.RawUrl })

在您的路线注册文件中,您现在可以编写调用相同操作的路线:

context.MapRoute(
                "Unprocessed_Details",
                "Unprocessed/Edit/{idSolicitor}/{action}",
                new { action = "Start", controller = "EditSolicitor", idSolicitor = UrlParameter.Optional }
            );

            context.MapRoute(
                "Inactive_Details",
                "Inactive/Edit/{idSolicitor}/{action}",
                new { controller = "EditSolicitor", action = "Start", idSolicitor = UrlParameter.Optional }
            );

            context.MapRoute(
                "Active_Details",
                "Solicitors/Edit/{idSolicitor}/{action}",
                new { controller = "EditSolicitor", action = "Start", idSolicitor = UrlParameter.Optional }
            );

正如您所看到的,这是三条路线调用的完全相同的动作。只要我在mvcSiteMapNode中指定路由名称,就可以在构建菜单时正确区分路由,并且突出显示可以根据需要进行。

关于使用XML困惑的注意事项:

我非常讨厌XML。它让我感到困惑和困惑,尤其是感冒时。

问题是将路由属性添加到mvcSiteMapNodes会增加可能的混乱。

我感到困惑

我最初尝试过卡洛斯的建议,但它没有用。不知道错误是什么,但是用牙齿梳理它并且现在正在工作。令人讨厌的是,我不确定我做错了什么。

生活在希望中

我希望这个文档尽管是mvcSiteMapNode的边缘方面。

答案 2 :(得分:0)

以防万一,您可以尝试以下方法:

context.MapRoute(
                    "Process_New_Members",
                    "Admin/{alternate}/Process/{MemberId}",
                    new { controller = "ProcessMembers", 
                        action = "Process", 
                        alternate="Unprocessed", 
                        MemberId = UrlParameter.Optional }
                );

通过这种方式,您可以通过要求此附加参数来区分每条路线。

答案 3 :(得分:0)

您可以在url中使用mvcSiteMapNode。如下所示:

<mvcSiteMapNode title="Process" area="Admin" controller="ProcessMembers" action="Process" aurl="/Admin/ProcessMembers/Process/Unprocessed"/>
<mvcSiteMapNode title="Change Status" area="Admin" controller="ProcessMembers" action="Process" url="/Admin/ProcessMembers/Process/Processed" />

当然,您应该使用适当的RoutConfig来使该网址起作用。 有一个示例here