C# 在MvcSiteMapProvider中为具有动态参数的DynamicNode创建子节点

C# 在MvcSiteMapProvider中为具有动态参数的DynamicNode创建子节点,c#,asp.net,asp.net-mvc,asp.net-mvc-2,mvcsitemapprovider,C#,Asp.net,Asp.net Mvc,Asp.net Mvc 2,Mvcsitemapprovider,我使用的是MVCSItemApprovider2.2.1(http://mvcsitemap.codeplex.com),并且当动态节点下的子节点具有动态参数(id)时,我在创建这些子节点(使用dynamicNodeProvider)时遇到问题 我在以下路线丢失面包屑: 商店/5/产品/编辑/23 其中url模式为: 存储/{storeID}/{controller}/{action}/{id} 当ID被忽略(即“新建”操作)时,它可以正常工作。但是当指定ID时,它与路由不匹配,并且我的面包屑(

我使用的是MVCSItemApprovider2.2.1(http://mvcsitemap.codeplex.com),并且当动态节点下的子节点具有动态参数(id)时,我在创建这些子节点(使用dynamicNodeProvider)时遇到问题

我在以下路线丢失面包屑:

商店/5/产品/编辑/23

其中url模式为:

存储/{storeID}/{controller}/{action}/{id}

当ID被忽略(即“新建”操作)时,它可以正常工作。但是当指定ID时,它与路由不匹配,并且我的面包屑(使用SiteMapPath帮助程序)为空

我的网站地图:(节选)

第一次尝试:

public override void RegisterArea(AreaRegistrationContext context)
{
        context.MapRoute(
            "Store_Index",
            "Stores",
            new { action = "Index", controller = "Home" },
            new string[] { "ControlPanel.Areas.Stores.Controllers" }
            );

        context.MapRoute(
            "Store_default",
            "Stores/{storeID}/{controller}/{action}/{id}",
            new { action = "Index", controller = "Manage", id = UrlParameter.Optional },
            new { storeID = @"\d+" },
            new string[] { "ControlPanel.Areas.Stores.Controllers" }
        );
    }
namespace ControlPanel
{
    public class CustomSiteMapNodeUrlResolver : ISiteMapNodeUrlResolver
    {
        public virtual string ResolveUrl(MvcSiteMapNode mvcSiteMapNode, string area, string controller, string action, IDictionary<string, object> routeValues)
        {
            RequestContext ctx;
            if (HttpContext.Current.Handler is MvcHandler)
                ctx = ((MvcHandler)HttpContext.Current.Handler).RequestContext;
            else
                ctx = new RequestContext(new HttpContextWrapper(HttpContext.Current), new RouteData());

            //Begin Added Code
            if (mvcSiteMapNode["dynamicParameters"] != null)
            {
                foreach (var item in mvcSiteMapNode["dynamicParameters"].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
                {
                    var dp = item.Trim();
                    routeValues[da] = ctx.RouteData.Values[dp];
                }
            }
            //End Added Code

            return new UrlHelper(ctx).Action(action, controller, new RouteValueDictionary(routeValues));
        }
    }
}
<siteMap defaultProvider="MvcSiteMapProvider" enabled="true">
  <providers>
    <clear/>
    <add name="MvcSiteMapProvider"
         type="MvcSiteMapProvider.DefaultSiteMapProvider, MvcSiteMapProvider"
         siteMapFile="~/Mvc.Sitemap"
         securityTrimmingEnabled="true"
         attributesToIgnore="visibility,dynamicParameters"
         scanAssembliesForSiteMapNodes="true" 
         siteMapNodeUrlResolver="ControlPanel.CustomSiteMapNodeUrlResolver, ControlPanel"
         siteMapNodeVisibilityProvider="MvcSiteMapProvider.FilteredSiteMapNodeVisibilityProvider, MvcSiteMapProvider" />
  </providers>
</siteMap>
DynamicNode node = new DynamicNode();
node.Attributes.Add("dynamicParameters", "id");
我尝试的第一件事是在站点地图xml中将子节点创建为动态节点的子节点。这根本不起作用,结果这些孩子成了“家”的孩子。我会在其中放一个ParentKey属性,除了每个存储都会重复这些属性,因此会有多个ParentKey

<?xml version="1.0" encoding="utf-8" ?>
<mvcSiteMap xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-2.0">
  <mvcSiteMapNode title="Home" controller="Dashboard" action="Index" changeFrequency="Always" updatePriority="Normal">
    <mvcSiteMapNode title="My Account" controller="Account" action="Index" key="Account" />
    <mvcSiteMapNode title="My Stores" area="Stores" controller="Home" action="Index" visibilityProvider="ControlPanel.Areas.Stores.StoreAreaVisibilityProvider, ControlPanel"  >
      <mvcSiteMapNode title="Store" action="Index" dynamicNodeProvider="ControlPanel.Areas.Stores.StoreAreaNodeProvider, ControlPanel">
        <mvcSiteMapNode title="Products" area="Stores" controller="Products" action="Index">
          <mvcSiteMapNode title="Edit" area="Stores" controller="Products" action="Edit"/>
          <mvcSiteMapNode title="New" area="Stores" controller="Products" action="Edit"/>
        </mvcSiteMapNode>
      </mvcSiteMapNode>
    </mvcSiteMapNode>
  </mvcSiteMapNode>
</mvcSiteMap>
如何在作为dynamicNode子节点的节点上模仿旧MvcSiteMapProvider的dynamicParameters属性?基本上,我需要它在匹配路由时忽略“id”路由值

希望我能正确地解释这一点,并且没有给你提供太多信息。谢谢


更新:

public override void RegisterArea(AreaRegistrationContext context)
{
        context.MapRoute(
            "Store_Index",
            "Stores",
            new { action = "Index", controller = "Home" },
            new string[] { "ControlPanel.Areas.Stores.Controllers" }
            );

        context.MapRoute(
            "Store_default",
            "Stores/{storeID}/{controller}/{action}/{id}",
            new { action = "Index", controller = "Manage", id = UrlParameter.Optional },
            new { storeID = @"\d+" },
            new string[] { "ControlPanel.Areas.Stores.Controllers" }
        );
    }
namespace ControlPanel
{
    public class CustomSiteMapNodeUrlResolver : ISiteMapNodeUrlResolver
    {
        public virtual string ResolveUrl(MvcSiteMapNode mvcSiteMapNode, string area, string controller, string action, IDictionary<string, object> routeValues)
        {
            RequestContext ctx;
            if (HttpContext.Current.Handler is MvcHandler)
                ctx = ((MvcHandler)HttpContext.Current.Handler).RequestContext;
            else
                ctx = new RequestContext(new HttpContextWrapper(HttpContext.Current), new RouteData());

            //Begin Added Code
            if (mvcSiteMapNode["dynamicParameters"] != null)
            {
                foreach (var item in mvcSiteMapNode["dynamicParameters"].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
                {
                    var dp = item.Trim();
                    routeValues[da] = ctx.RouteData.Values[dp];
                }
            }
            //End Added Code

            return new UrlHelper(ctx).Action(action, controller, new RouteValueDictionary(routeValues));
        }
    }
}
<siteMap defaultProvider="MvcSiteMapProvider" enabled="true">
  <providers>
    <clear/>
    <add name="MvcSiteMapProvider"
         type="MvcSiteMapProvider.DefaultSiteMapProvider, MvcSiteMapProvider"
         siteMapFile="~/Mvc.Sitemap"
         securityTrimmingEnabled="true"
         attributesToIgnore="visibility,dynamicParameters"
         scanAssembliesForSiteMapNodes="true" 
         siteMapNodeUrlResolver="ControlPanel.CustomSiteMapNodeUrlResolver, ControlPanel"
         siteMapNodeVisibilityProvider="MvcSiteMapProvider.FilteredSiteMapNodeVisibilityProvider, MvcSiteMapProvider" />
  </providers>
</siteMap>
DynamicNode node = new DynamicNode();
node.Attributes.Add("dynamicParameters", "id");
以下是基于Jakub的答案对我有效的解决方案

在MvcSiteMapProvider 2.x中,您可以自行实现IsItemAppNodeUrlResolver,而无需修改源代码。所以我基本上增加了dynamicParameters属性的功能

类别:

public override void RegisterArea(AreaRegistrationContext context)
{
        context.MapRoute(
            "Store_Index",
            "Stores",
            new { action = "Index", controller = "Home" },
            new string[] { "ControlPanel.Areas.Stores.Controllers" }
            );

        context.MapRoute(
            "Store_default",
            "Stores/{storeID}/{controller}/{action}/{id}",
            new { action = "Index", controller = "Manage", id = UrlParameter.Optional },
            new { storeID = @"\d+" },
            new string[] { "ControlPanel.Areas.Stores.Controllers" }
        );
    }
namespace ControlPanel
{
    public class CustomSiteMapNodeUrlResolver : ISiteMapNodeUrlResolver
    {
        public virtual string ResolveUrl(MvcSiteMapNode mvcSiteMapNode, string area, string controller, string action, IDictionary<string, object> routeValues)
        {
            RequestContext ctx;
            if (HttpContext.Current.Handler is MvcHandler)
                ctx = ((MvcHandler)HttpContext.Current.Handler).RequestContext;
            else
                ctx = new RequestContext(new HttpContextWrapper(HttpContext.Current), new RouteData());

            //Begin Added Code
            if (mvcSiteMapNode["dynamicParameters"] != null)
            {
                foreach (var item in mvcSiteMapNode["dynamicParameters"].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
                {
                    var dp = item.Trim();
                    routeValues[da] = ctx.RouteData.Values[dp];
                }
            }
            //End Added Code

            return new UrlHelper(ctx).Action(action, controller, new RouteValueDictionary(routeValues));
        }
    }
}
<siteMap defaultProvider="MvcSiteMapProvider" enabled="true">
  <providers>
    <clear/>
    <add name="MvcSiteMapProvider"
         type="MvcSiteMapProvider.DefaultSiteMapProvider, MvcSiteMapProvider"
         siteMapFile="~/Mvc.Sitemap"
         securityTrimmingEnabled="true"
         attributesToIgnore="visibility,dynamicParameters"
         scanAssembliesForSiteMapNodes="true" 
         siteMapNodeUrlResolver="ControlPanel.CustomSiteMapNodeUrlResolver, ControlPanel"
         siteMapNodeVisibilityProvider="MvcSiteMapProvider.FilteredSiteMapNodeVisibilityProvider, MvcSiteMapProvider" />
  </providers>
</siteMap>
DynamicNode node = new DynamicNode();
node.Attributes.Add("dynamicParameters", "id");
我使用的是1.x版。 我对动态参数也有类似的问题

我不得不修改源代码-在mvcsitemanode.cs中做了一个更改。这是Url属性的新实现

    public override string Url
    {
        get
        {
            if (!string.IsNullOrEmpty(this.url))
                return this.url;

            RequestContext ctx;
            if (HttpContext.Current.Handler is MvcHandler)
                ctx = ((MvcHandler)HttpContext.Current.Handler).RequestContext;
            else
                ctx = new RequestContext(new HttpContextWrapper(HttpContext.Current), new RouteData());

            var routeValues = new RouteValueDictionary(RouteValues);

            foreach (var key in DynamicParameters)
                routeValues.Add(key, ctx.RouteData.Values[key]);

            return new UrlHelper(ctx).Action(Action, Controller, routeValues);
        }
        set
        {
            this.url = value;
        }
    }
请注意dynamicParameters的实际值是如何添加到routeValues集合的

上述更改允许我在sitemap中定义动态paremtere(如“id”),并创建带有Account/User/Edit/23等链接的面包屑

我简要介绍了2.x版,您应该能够应用类似的补丁。
希望它能帮助你……

太棒了。。。非常感谢你。我采纳了你的建议,并将其修改为适合2.x的风格,并将其作为另一个答案发布。另外。。。这里的惯例是什么?我是将你的答案标记为已接受的答案(我想做的),还是标记我的答案,因为它包含对我有用的代码?@Chris-你如何编辑你的问题并将最后一段代码发布在那里?另外,我很高兴它对您起到了作用。现在是2012年,我使用的是3.x版,但仍然存在相同的问题。问题是,应用程序启动后,您的自定义ResolveUrl方法就会运行(不导航到该参数化页面),因此您获得的当前路由参数ctx.RoutedData.Values[dp]的行为空,因此,无法用当前路由参数替换站点地图路由参数。有什么想法吗?嗨@AlperOzcetin,我很久没有接触过这个代码了,我不知道。事实上,在这篇文章发表几个月后,这个项目发展到MvcSiteMapProvider没有为我做这件事的地步,我们最终从零开始推出了自己的解决方案(该网站至今仍在运行)。祝你好运