【问题标题】:Can MVC SiteMapProvider dynamicNode be used on 1st Level?可以在第一级使用 MVC SiteMapProvider dynamicNode 吗?
【发布时间】:2015-10-15 22:22:52
【问题描述】:

我已经用 MVC SiteMapProvider 搞砸了一段时间,我很喜欢它。我正在建立一个电子商务网站,到目前为止,它在我的开发过程中运行良好。

我似乎无法解决的一个问题是如何让 dynamicNode 在第一级工作。

类似这样的:

www.mysite.com/{type}/{category}/{filter}

只有 3 种类型,所以现在我只有 3 个以该类型命名的控制器,它们都使用相同的逻辑和视图模型,这对于未来的可维护性来说并不是一个理想的设置。我的 routeConfig 包括 3 条这样的路线。

routes.MapRoute(
            name: "Hardscape",
            url: "hardscape-products/{category}/{filter}",
            defaults: new { controller = "Products", action = "Index", category = UrlParameter.Optional, filter = UrlParameter.Optional},
            namespaces: new[] { "MyApp.Web.Controllers" }
        );

routes.MapRoute(
            name: "Masonry",
            url: "masonry-products/{category}/{filter}",
            defaults: new { controller = "Products", action = "Index", category = UrlParameter.Optional, filter = UrlParameter.Optional},
            namespaces: new[] { "MyApp.Web.Controllers" }
        );

routes.MapRoute(
            name: "Landscape",
            url: "landscape-products/{category}/{filter}",
            defaults: new { controller = "Products", action = "Index", category = UrlParameter.Optional, filter = UrlParameter.Optional},
            namespaces: new[] { "MyApp.Web.Controllers" }
        );

我尝试过类似的方法,但它返回 404。

   routes.MapRoute(
            name: "Products",
            url: "{productType}/{category}/{filter}",
            defaults: new { controller = "Products", action = "Index", productType = UrlParameter.Optional,  category = UrlParameter.Optional, filter = UrlParameter.Optional},
            namespaces: new[] { "MyApp.Web.Controllers" }
        );

我已经能够为我的类别和过滤器参数使用 dynamicNode 在站点地图和菜单中生成我的节点。当我没有静态命名第一级时,只是遇到了第一级的问题

masonry-products/ vs. {productType}/

如果您有解决方案,请告诉我。希望 NightOwl 能加入进来。

【问题讨论】:

  • 请从您的标题中删除“已解决”并从您的问题中删除解决方案。如果您自己的解决方案与接受的答案不同,您可以随意将其添加为单独的答案。
  • 感谢您的建议。

标签: asp.net-mvc asp.net-mvc-5 mvcsitemapprovider


【解决方案1】:

.NET 的路由框架非常灵活。

对于这种情况,您可以只对类型使用约束。有两种方式:

  1. Use a RegEx
  2. Implement a custom class

如果您不期望有很多变化,第一个选项也不会那么糟糕:

routes.MapRoute(
    name: "Products",
    url: "{productType}/{category}/{filter}",
    defaults: new { controller = "Products", action = "Index", category = UrlParameter.Optional, filter = UrlParameter.Optional},
    constraints: new { productType = @"hardscape-products|masonry-products|landscape-products" },
    namespaces: new[] { "MyApp.Web.Controllers" }
);

第二个选项更动态:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Routing;

public class ProductTypeConstraint : IRouteConstraint
{
    private object synclock = new object();

    public bool Match
        (
            HttpContextBase httpContext,
            Route route,
            string parameterName,
            RouteValueDictionary values,
            RouteDirection routeDirection
        )
    {
        return GetProductTypes(httpContext).Contains(values[parameterName]);
    }

    private IEnumerable<string> GetProductTypes(HttpContextBase httpContext)
    {
        string key = "ProductTypeConstraint_GetProductTypes";
        var productTypes = httpContext.Cache[key];
        if (productTypes == null)
        {
            lock (synclock)
            {
                productTypes = httpContext.Cache[key];
                if (productTypes == null)
                {
                    // TODO: Retrieve the list of Product types from the 
                    // database or configuration file here.
                    productTypes = new List<string>()
                    {
                        "hardscape-products",
                        "masonry-products",
                        "landscape-products"
                    };

                    httpContext.Cache.Insert(
                        key: key,
                        value: productTypes,
                        dependencies: null,
                        absoluteExpiration: System.Web.Caching.Cache.NoAbsoluteExpiration,
                        slidingExpiration: TimeSpan.FromMinutes(15),
                        priority: System.Web.Caching.CacheItemPriority.NotRemovable,
                        onRemoveCallback: null);
                }
            }
        }

        return (IEnumerable<string>)productTypes;
    }
}

这里需要缓存,因为每个请求都会受到约束。

routes.MapRoute(
    name: "Products",
    url: "{productType}/{category}/{filter}",
    defaults: new { controller = "Products", action = "Index", category = UrlParameter.Optional, filter = UrlParameter.Optional},
    constraints: new { productType = new ProductTypeConstraint() },
    namespaces: new[] { "MyApp.Web.Controllers" }
);

当然,这不是唯一的动态选项。如果您确实需要选择您选择的任何 URL,例如在 CMS 中,您可以inherit RouteBase 并从数据库中驱动所有 URL。

不过,不确定这个问题与动态节点提供程序有什么关系。我也不明白“一级”是什么意思。

您真正需要对动态节点提供程序做的唯一事情就是匹配您在路由中拥有的相同路由值并提供键-父键关系。必须在 XML 或 .NET 属性中定义父键才能附加来自提供程序的顶级节点。

路由

dynamicNode.Controller = "Product";
dynamicNode.Action = "Index";
dynamicNode.RouteValues.Add("productType", "hardscape-products");
dynamicNode.RouteValues.Add("category", "some-category");
dynamicNode.RouteValues.Add("filter", "some-filter");

dynamicNode.Controller = "Product";
dynamicNode.Action = "Index";
dynamicNode.PreservedRouteParameters = new string[] { "productType", "category", "filter" };

对您的应用程序有意义的路由值和保留的路由参数的某种组合。

有关这些选项的说明,请阅读How to Make MvcSiteMapProvider Remember a User's Position

键匹配

// This assumes you have explicitly set a key to "Home"
// in a node outside of the dynamic node provider.
dynamicNode.ParentKey = "Home";
dynamicNode.Key = "Product1";

// This node has the node declared above
// as its parent.
dynamicNode.ParentKey = "Product1";
dynamicNode.Key = "Product1Details";

【讨论】:

  • 感谢您的快速回复。我真的很感谢你的帮助。你说得对,“第一级”的描述太模糊了。我正在根据菜单树直观地考虑问题。解决方案最终成为您提供的具有固定约束值的方法。在我之前的尝试中,我只是试图将字符串限制为最少的字符数。我提供了最终解决方案的示例,其中包括我的动态节点。将来我可能会添加您的其他建议。再次感谢!!
【解决方案2】:

OP 的解决方案。

非常感谢 NightOwl888 的详细回答,帮助我解决了这个问题。我之前关注过一个 MSDN 教程 here,我认为这让我对约束的使用感到困惑。

总而言之,我没有正确定义我的约束,这导致了 404 以及我与 MVCSiteMapProvider 相关的所有其他问题。这是工作解决方案的示例。

路线

routes.MapRoute(
         name: "Products",
         url: "{productType}/{category}/{filter}/{filterAction}/{filterId}",
         defaults: new { controller = "Products", action = "Index", productType = UrlParameter.Optional, category = UrlParameter.Optional, filter = UrlParameter.Optional, filterAction = UrlParameter.Optional, filterId = UrlParameter.Optional },
         constraints: new { productType = @"building-products|installation-materials|tools" },
        namespaces: new[] { "MyApp.Web.Controllers" }
        );

XML

<mvcSiteMapNode title="Product Type" dynamicNodeProvider="MyApp.Web.SiteMapProviders.ProductTypeSiteMapProvider, MyApp.Web">
  <mvcSiteMapNode title="Category" dynamicNodeProvider="MyApp.Web.SiteMapProviders.CategorySiteMapProvider, MyApp.Web">
    <mvcSiteMapNode title="Option" dynamicNodeProvider="MyApp.Web.SiteMapProviders.OptionSiteMapProvider, MyApp.Web" />
    <mvcSiteMapNode title="Association" dynamicNodeProvider="MyApp.Web.SiteMapProviders.AssociationSiteMapProvider, MyApp.Web" />
  </mvcSiteMapNode>
</mvcSiteMapNode>

4 个动态节点中的前 2 个给你一个想法

public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)
    {
        using (var db = new ProductContext())
        {              
            foreach (var productType in db.ProductTypes.ToList())
            {
                DynamicNode dynamicNode = new DynamicNode();
                dynamicNode.Key = productType.Name.ToLower().Replace(" ", "-");
                dynamicNode.Title = productType.Name;
                dynamicNode.Clickable = false;
                yield return dynamicNode;
            }
        }
    }

public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)
    {
        using (var db = new ProductContext())
        {              
            foreach (var category in db.Categories.ToList())
            {
                DynamicNode dynamicNode = new DynamicNode();
                dynamicNode.Key = category.Name.Replace(" ", "");
                dynamicNode.Title = category.Name;
                dynamicNode.Controller = "Products";
                dynamicNode.Action = "Index";
                dynamicNode.ParentKey = category.ProductType.Name.ToLower().Replace(" ", "-");
                dynamicNode.RouteValues.Add("productType", category.ProductType.Name.ToLower().Replace(" ", "-"));
                dynamicNode.RouteValues.Add("category", category.Name.ToLower().Replace(" ", "-"));
                dynamicNode.ImageUrl = category.CategoryImage();
                yield return dynamicNode;
            }
        }
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-08
    • 2015-11-23
    • 1970-01-01
    • 2011-12-01
    • 1970-01-01
    相关资源
    最近更新 更多