【问题标题】:MvcSiteMapProvider ISiteMapBuilder in conjunction with IDynamicNodeProviderMvcSiteMapProvider ISiteMapBuilder 与 IDynamicNodeProvider 结合使用
【发布时间】:2013-09-30 10:11:26
【问题描述】:

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

这是可行的,这是返回的基本结构:

  • 首页
    • 新闻
    • 产品
    • 关于
    • 联系方式

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

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

查看源代码,我看到了与DynamicNodeProviderStrategy 相关的PluginProvider-stuff,然后你去了,他们失去了我......

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

【问题讨论】:

    标签: asp.net-mvc mvcsitemapprovider


    【解决方案1】:

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

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

    1. 节点已正确映射到其父节点
    2. 只有 1 个根节点
    3. 所有节点都添加到站点地图中
    4. 在站点地图完全构建后最后执行访问者

    添加多个 ISiteMapBuilder 实例是没有意义的,因为这样可以绕过这一重要逻辑。因此,最好不要实现 ISiteMapBuilder,而是实现 ISiteMapNodeProvider。

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

    ISiteMapNodeProvider 接口如下所示:

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

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

    该类的级别低于 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”,所有类别都将是该节点的子级。您当然可以根据自己的需要进行调整。

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

    有关此接口的其他实现示例,请参阅XmlSiteMapNodeProviderReflectionSiteMapNodeProvider

    【讨论】:

    • 在我的解决方案中经过一番挣扎后,我设法通过您的示例中的 nodeprovider 获取节点!之后,我将该提供程序挂到一个新的提供程序中,该提供程序 provides 来自Pages 表的节点,以便很好地工作。问题是我确实需要一个ISiteMapBuilder 实现来将此站点地图提供给我的管理界面。感谢您为我指明正确的方向。
    • 您是如何连接的,以便使用新的 CustomSiteMapNodeProvider?
    • @retry - 如果在应用程序中使用单个 SiteMap,您只需将其添加到 CompositeSiteMapNodeProvider(一个数组)的构造函数中。如果您的目标是使用多个 SiteMap 实例,这里有一个 Unity 示例:github.com/maartenba/MvcSiteMapProvider/issues/…
    猜你喜欢
    • 1970-01-01
    • 2016-12-16
    • 2013-01-10
    • 2018-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-30
    相关资源
    最近更新 更多