MvcSiteMapProvider ISiteMapBuilder与IDynamicNodeProvider结合使用

时间:2013-09-30 10:11:27

标签: asp.net-mvc mvcsitemapprovider

我正在使用MvcSiteMapProvider 4.4.3从数据库动态构建站点地图。我正在关注这篇文章:https://github.com/maartenba/MvcSiteMapProvider/wiki/Multiple-Sitemaps-in-One-Application,因为我正在使用多个站点地图。

这是有效的,这是返回的基本结构:

  • 主页
    • 新闻
    • 产品
    • 关于
    • 联系

应根据不同的数据再次动态填充其中一个节点(/Products)。为此,我需要IDynamicNodeProvider节点上的/Products实现? (如果我错了请纠正我吗?

无论如何,我想我确实需要上述内容。文档显示了在XML中定义的节点和使用控制器操作的属性定义的节点上执行此操作的方法,但不是ISiteMapBuilder中的“手动”。因此,如果我设置.DynamicNodeProvider实例的ISiteMapNode属性,它似乎不会被实例化...... .HasDynamicNodeProvider属性也会返回false

查看来源,我看到PluginProvider - 与DynamicNodeProviderStrategy相关的内容,你去了,他们已经失去了我......

如何在ISiteMapNode中为“/Products”创建ISiteMapBuilder,以便它的后代(/Products/Cat/Products/Cat/Product)是动态的从数据库加载?

1 个答案:

答案 0 :(得分:2)

您可以使用ISiteMapBuilder执行此操作,但您可能最好不要实现ISiteMapNodeProvider。原因是因为在实例化所有节点之后,必须在流程的最后添加节点到SiteMap,以确保每个节点都正确映射到父节点(当然,根节点不是需要父母)。这是4.3.0中进行的主要设计更改。

现在已经设置了默认的SiteMapBuilder类以确保

  1. 节点已正确映射到其父节点
  2. 只有一个根节点
  3. 所有节点都已添加到SiteMap
  4. 完全构建SiteMap后,最后执行访问者
  5. 添加多个ISiteMapBuilder实例是没有意义的,因为这样可以绕过这个重要的逻辑。因此,最好不要实现ISiteMapBuilder,而是实现ISiteMapNodeProvider。

    SiteMapBuilder类通过其构造函数将ISiteMapNodeProvider作为依赖项。您可以使用CompositeSiteMapNodeProvider类来处理此接口上的多重性,以便在需要时添加多个ISiteMapNodeProvider实现。

    ISiteMapNodeProvider界面如下所示:

    public interface ISiteMapNodeProvider
    {
        IEnumerable<ISiteMapNodeToParentRelation> GetSiteMapNodes(ISiteMapNodeHelper helper);
    }
    

    只有一种方法可以实现。此外,许多常见(但可选)服务是通过接口自动从SiteMapBuilder类通过ISiteMapNodeHelper注入的。

    此类的级别低于IDynamicNodeProvider。您正在直接与ISiteMapNode交互,但与SiteMap类的所有交互都由SiteMapBuilder处理。 ISiteMapNode包装在ISiteMapNodeToParentRelation实例中,该实例用于确保可以跟踪其父节点密钥,直到将其添加到SiteMap对象为止。

    您的SiteMapNodeProvider应如下所示:

    public class CustomSiteMapNodeProvider
        : ISiteMapNodeProvider
    {
        private readonly string sourceName = "CustomSiteMapNodeProvider";
    
        #region ISiteMapNodeProvider Members
    
        public IEnumerable<ISiteMapNodeToParentRelation> GetSiteMapNodes(ISiteMapNodeHelper helper)
        {
            var result = new List<ISiteMapNodeToParentRelation>();
    
            using (var db = new DatabaseContextClass())
            {
                foreach (var category in db.Categories.ToList())
                {
                    var categoryRelation = this.GetCategoryRelation("Products", category, helper);
                    result.Add(categoryRelation);
    
    
                }
    
                foreach (var product in db.Products.Include("Category"))
                {
                    var productRelation = this.GetProductRelation("Category_" + product.CategoryId, product, helper);
                    result.Add(productRelation);
                }
            }
    
            return result;
        }
    
        #endregion
    
        protected virtual ISiteMapNodeToParentRelation GetCategoryRelation(string parentKey, Category category, ISiteMapNodeHelper helper)
        {
            string key = "Category_" + category.Id;
            var result = helper.CreateNode(key, parentKey, this.sourceName);
            var node = result.Node;
    
            node.Title = category.Name;
    
            // Populate other node properties here
    
            // Important - always set up your routes (including any custom params)
            node.Area = "MyArea"; // Required - set to empty string if not using areas
            node.Controller = "Category"; // Required
            node.Action = "Index"; // Required
            node.RouteValues.Add("id", category.Id.ToString());
    
            return result;
        }
    
        protected virtual ISiteMapNodeToParentRelation GetProductRelation(string parentKey, Product product, ISiteMapNodeHelper helper)
        {
            string key = "Product_" + product.Id;
            var result = helper.CreateNode(key, parentKey, this.sourceName);
            var node = result.Node;
    
            node.Title = product.Name;
    
            // Populate other node properties here
    
            // Important - always set up your routes (including any custom params)
            node.Area = "MyArea"; // Required - set to empty string if not using areas
            node.Controller = "Product"; // Required
            node.Action = "Index"; // Required
            node.RouteValues.Add("id", product.Id.ToString());
            node.RouteValues.Add("categoryId", product.CategoryId.ToString()); // Optional - use if you have a many-to-many relationship.
    
            return result;
        }
    }
    

    上面的示例假设您已通过其他方式添加了一个节点,其中键设置为“Products”,所有类别都将属于其中。您当然可以根据自己的需要进行调整。

    通常最好只实现此接口一次并使用单个数据库连接来加载整个SiteMap。您始终可以将其重构为多个类,每个类处理接口侧的单个表以分离关注点。但通常最好将相关实体之间的所有键映射逻辑放在一起,以便于维护。

    有关此界面实现的其他示例,请参阅XmlSiteMapNodeProviderReflectionSiteMapNodeProvider