代码之家  ›  专栏  ›  技术社区  ›  Chris Curtis

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

  •  19
  • Chris Curtis  · 技术社区  · 14 年前

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

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

    商店/5/产品/Edit/23

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

    当ID被遗漏时(即“New”动作),它工作得很好。但是当指定ID时,它与路由不匹配,并且我的breadcrumbs(使用SiteMapPath助手)为空。

    我的站点地图: (删节)

    <?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>
        </mvcSiteMapNode>
    </mvcSiteMap>
    

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

    首次尝试:

    我尝试的第一件事是在sitemap 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>
    

    第二次尝试:

    下一个选项似乎是直接在DynamicNodeProvider中添加子节点。除了具有动态参数的节点外,这种方法的效果更好

    (为了便于解释,修改了下面的内容…)

    public class StoreAreaNodeProvider : IDynamicNodeProvider
    {
        public IEnumerable<DynamicNode> GetDynamicNodeCollection()
        {
            var nodes = new List<DynamicNode>();
    
            foreach (var store in repo.GetStores())
            {
                DynamicNode node = new DynamicNode();
                node.Title = store.Name;
                node.Area = "Stores";
                node.Controller = "Manage";
                node.Action = "Index";
                node.RouteValues.Add("storeID", store.StoreID);
                node.Key = "Store_" + store.StoreID.ToString();
    
                nodes.Add(node);
    
                //Child of node
                DynamicNode productsNode = new DynamicNode();
                productsNode.Title = "Products";
                productsNode.Area = "Stores";
                productsNode.Controller = "Products";
                productsNode.Action = "Index";
                productsNode.RouteValues.Add("storeID", store.StoreID);
                productsNode.ParentKey = String.Format("Store_{0}", store.StoreID.ToString());
                productsNode.Key = String.Format("Store_{0}_Products", store.StoreID.ToString());
    
                nodes.Add(productsNode);
    
                //child of productsNode
                DynamicNode editNode = new DynamicNode();
                editNode.Title = "Edit";
                editNode.Area = "Stores";
                editNode.Action = "Edit";
                editNode.Controller = "Products";
                editNode.RouteValues.Add("storeID", store.StoreID);
                //I can't add the RouteValue "ID" here because it is dynamic
                //I would have do loop through every product for this store with
                //another dynamic node provider, but that seems terribly inefficient and stupid
                editNode.ParentKey = String.Format("Store_{0}_Products", store.StoreID.ToString());
                editNode.Attributes.Add("visibility", "SiteMapPathHelper,!*");
    
                nodes.Add(editNode);
            }
    
            return nodes;
        }
    }
    

    总结

    是否有效:
    不起作用:
    对于Url模式: 存储/{storeID}/{controller}/{action}/{id}

    我想做的是:

    editNode.Attributes.Add("isDynamic", "true");
    editNode.Attributes.Add("dynamicParameters", "id");
    

    如何在dynamicode的子节点上模仿旧的mvcsitemprovider的dynamicParameters属性?基本上,我需要它在匹配路由时忽略“id”路由值。


    更新:

    以下是根据Jakub的回答为我提供的解决方案。

    班级:

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

    Web.config:

    <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 回复  |  直到 14 年前
        1
  •  8
  •   Jakub Konecki    14 年前

    我使用的是1.x版。 我对动态参数也有类似的问题。

    我不得不修改源代码-在MvcSiteMapNode.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;
            }
        }
    

    上面的更改允许我在sitemap中定义动态paremteres(比如'id'),并创建带有Account/User/Edit/23链接的breadcrumb。

    我简单地看了一下2.x版,您应该可以应用类似的补丁。