6

设置:

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

我有一个自定义菜单生成器,它评估一个节点是否在当前分支上(即,如果它SiteMap.CurrentNode是 CurrentNode 或 CurrentNode 是否嵌套在它下面)。代码包含在下面,但本质上是检查每个节点的 url 并将其与 currentnode 的 url 进行比较,直至 currentnodes “家谱”。

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

问题:

我的自定义菜单工作正常,但我发现mvcSiteMapProvider似乎并没有以一致的方式评估url的:CurrentNode

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

问题示例:

在我拥有的应用程序中Members

成员具有MemberStatus可以是“未处理”、“活动”或“非活动”的字段。要更改 MemberStatus,我有一个ProcessMemberController名为 Admin 的区域。处理是使用 上的Process操作完成的ProcessMemberController

我的 mvcSiteMap 有两个节点都映射到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" />

路线:

到这两个节点的对应路由是(同样,唯一能区分它们的是alternate 参数的值):

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 助手使用路由并生成我期望的 url:

@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

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

什么不起作用:

假设我的请求涉及第二个选项,即 ,/Admin/Members/Status/Change/47对应于 alternate = "Processed" 和 MemberId 47。

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

PreviousSibling: null
Provider: {MvcSiteMapProvider.DefaultSiteMapProvider}
ReadOnly: false
ResourceKey: ""
Roles: Count = 0
RootNode: {Home}
Title: "Process"
Url: "/Admin/Unprocessed/Process/47"

即,对于 /Admin/Members/Status/Change/47 的请求 url,SiteMap.CurrentNode.Url计算结果为/Admin/Unprocessed/Process/47. 即,它忽略了alternate参数并使用了错误的路线。

CurrentBranch 静态属性:

/// <summary>
        /// ReadOnly. Gets the Branch of the Site Map that holds the SiteMap.CurrentNode
        /// </summary>
        public static List<SiteMapNode> CurrentBranch
        {
            get
            {
                List<SiteMapNode> currentBranch = null;
                if (currentBranch == null)
                {
                    SiteMapNode cn = SiteMap.CurrentNode;
                    SiteMapNode n = cn;
                    List<SiteMapNode> ln = new List<SiteMapNode>();
                    if (cn != null)
                    {
                        while (n != null && n.Url != SiteMap.RootNode.Url)
                        {
                            // I don't need to check for n.ParentNode == null
                            // because cn != null && n != SiteMap.RootNode
                            ln.Add(n);
                            n = n.ParentNode;
                        }
                        // the while loop excludes the root node, so add it here
                        // I could add n, that should now be equal to SiteMap.RootNode, but this is clearer
                        ln.Add(SiteMap.RootNode);

                        // The nodes were added in reverse order, from the CurrentNode up, so reverse them.
                        ln.Reverse();
                    }
                    currentBranch = ln;
                }
                return currentBranch;
            }
        }

问题:

我究竟做错了什么?

正如我所料,这些路线由 Html.ActionLlink 解释,但不像我所料那样由 SiteMap.CurrentNode 评估。换句话说,在评估我的路线时,SiteMap.CurrentNode 忽略了可区分的备用参数。

4

4 回答 4

3

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

正确的方法是按名称处理路线。所以站点地图应该引用一个路由名称而不是控制器、操作等。

于 2012-10-08T09:30:54.520 回答
2

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

最大的绊脚石:

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

你必须给不同的节点一个密钥!!!否则站点地图将不会呈现!

例如,仅路由属性本身是不够的,您必须为指向同一操作的每个节点分配一个唯一键以将它们彼此区分开:

<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 动作。我将其更改为对不同操作的调用。但是在编辑我的对象(律师)时,我不能这样做,因为编辑是由一个 MVC 向导完成的,就像我的自定义菜单构建器一样,我已经编写并且工作得很好。根本不可能(或者更确切地说,干)重新创建向导三次。所以我必须让我的菜单突出显示在指向同一动作的不同路线上正常工作。

没有 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 增加了混淆的可能性。

我很困惑我得到了

我最初尝试了 Carlos 的建议,但没有奏效。不知道错误是什么,但用梳子梳理了一下,现在可以正常工作了。令人讨厌的是,我不确定自己做错了什么。

活在希望中:

我希望这记录了 mvcSiteMapNode 的一个边缘方面。

于 2012-10-09T12:22:09.993 回答
0

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

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

这样,您将通过需要此附加参数来区分每条路线。

于 2012-10-08T09:35:08.783 回答
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的 url 来工作。这里有一个样本。

于 2019-05-15T08:22:29.380 回答