【问题标题】:Using MvcSiteMapProvider with Attributes and Attribute Routing将 MvcSiteMapProvider 与属性和属性路由一起使用
【发布时间】:2014-10-30 04:57:41
【问题描述】:

我正在尝试在我的 ASP.Net MVC 5 应用程序中使用 MVCSiteMapProvider。可以找到很多资源和教程,但其中大部分是关于基于 XML 的配置。

在我的应用程序中,属性路由已被使用,我想将 MvcSiteMapProvider 与属性一起使用,但是没有足够的资源用于这种组合,我遇到了一些问题。

例如我有如下三个动作:

//HomeController    
    [Route(@"~/home", Name = "CustomerHomeIndex")]
        [MvcSiteMapNode(Title = "Home Page",  Key = "Home")] 
        public ActionResult Index() {
            return View()
        }

//AccountController       
        [Route(@"~/account", Name = "AccountIndex")]
        [MvcSiteMapNode(Title = "Accounts", ParentKey = "Home", Key = "AccountIndex")] 
        public ActionResult Index() {
        // fetching records from database
            return View();
        }

        [Route(@"~/account-management/{id:int}/{domain:regex(^([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$)}", Name = "AccountDetail")]
        [MvcSiteMapNode(Title = "Account Detail", ParentKey = "AccountIndex", Key = "AccountDetail")] 
        public ActionResult Details(string domain, int id) {
        // fetching record from database by parameters
            return View();
        }

我还在视图中添加了 SiteMapPath 代码

//Details.cshtml
    @Html.MvcSiteMap().SiteMapPath()

但结果没有显示任何内容。在我看来,它是关于 preservedRouteParameters 的,但我找不到关于 MvcSiteMapNode 属性中使用的这个参数的任何信息。

实际上我还有一个关于本地化的问题,我想从资源文件中获取标题,所有内容都已经存在于全局资源文件中。我读过一些关于本地化支持的文章,但它们也与基于 XML 的配置有关。

【问题讨论】:

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


    【解决方案1】:

    默认情况下,XML 和 .NET 属性的提供程序均已启用。在此配置中,根节点(没有父键的节点)必须放在 XML 文件中。要在 XML 中不进行任何配置而独占使用 .NET 属性,您需要从配置中删除 XML 节点提供程序。

    内部 DI:

    <appSettings>
        <add key="MvcSiteMapProvider_EnableSiteMapFile" value="false"/>
    </appSettings>
    

    外部 DI(显示结构图示例):

    // Register the sitemap node providers
    var siteMapNodeProvider = this.For<ISiteMapNodeProvider>().Use<CompositeSiteMapNodeProvider>()
        .EnumerableOf<ISiteMapNodeProvider>().Contains(x =>
        {
            //Remove the XmlSiteMapNodeProvider
            //x.Type<XmlSiteMapNodeProvider>()
            //    .Ctor<bool>("includeRootNode").Is(true)
            //    .Ctor<bool>("useNestedDynamicNodeRecursion").Is(false)
            //    .Ctor<IXmlSource>().Is(xmlSource);
            x.Type<ReflectionSiteMapNodeProvider>()
                .Ctor<IEnumerable<string>>("includeAssemblies").Is(includeAssembliesForScan)
                .Ctor<IEnumerable<string>>("excludeAssemblies").Is(new string[0]);
        });
    

    您还需要确保包含控制器的程序集包含在 IncludeAssembliesForScan 配置设置中。请注意,NuGet 包会自动包含您将 MvcSiteMapProvider 安装到的程序集,因此如果您的控制器都在您的主 MVC 项目中,则无需触摸它。

    内部 DI:

    <appSettings>
        <add key="MvcSiteMapProvider_IncludeAssembliesForScan" value="MyAssembly,MyOtherAssembly"/>
    </appSettings>
    

    外部 DI:

    string[] includeAssembliesForScan = new string[] { "MyAssembly", "MyOtherAssembly" };
    
    ... Other code omitted ...
    
    // Register the sitemap node providers
    var siteMapNodeProvider = this.For<ISiteMapNodeProvider>().Use<CompositeSiteMapNodeProvider>()
        .EnumerableOf<ISiteMapNodeProvider>().Contains(x =>
        {
            //Remove the XmlSiteMapNodeProvider
            //x.Type<XmlSiteMapNodeProvider>()
            //    .Ctor<bool>("includeRootNode").Is(true)
            //    .Ctor<bool>("useNestedDynamicNodeRecursion").Is(false)
            //    .Ctor<IXmlSource>().Is(xmlSource);
            x.Type<ReflectionSiteMapNodeProvider>()
                .Ctor<IEnumerable<string>>("includeAssemblies").Is(includeAssembliesForScan) // <- Setting is injected here
                .Ctor<IEnumerable<string>>("excludeAssemblies").Is(new string[0]);
        });
    

    您不需要做任何特别的事情来使其与 AttributeRouting 一起使用 - MvcSiteMapProvider 会自动选择这些路由,只要您正确配置它们并在 MVC 中工作,它应该可以正常工作。

    是的,您可能确实需要为包含自定义参数的操作使用 PreservedRouteParameters,就像这样。

    [Route(@"~/account-management/{id:int}/{domain:regex(^([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$)}", Name = "AccountDetail")]
    [MvcSiteMapNode(Title = "Account Detail", ParentKey = "AccountIndex", Key = "AccountDetail", PreservedRouteParameters="domain,id")] 
    public ActionResult Details(string domain, int id) {
    // fetching record from database by parameters
        return View();
    }
    

    在您的简单示例中,这将正常工作。但是,当嵌套超过第一个节点的级别时,您需要完全了解preservedRouteParameters 如何正确使用它们。您不能使用具有相同键名但含义不同的参数在同一请求中都可见,因为 MvcSiteMapProvider 总是将当前请求中的值插入到具有匹配键名的所有节点中。您还必须在(子节点的)请求中提供祖先节点所需的任何键,以便导航正常工作。请参阅How to Make MvcSiteMapProvider Remember a User's Position 和演示代码了解完整详情。

    查看reading localization from an external assembly。但是,请注意,从 v4.6.15 开始,唯一可行的方法是使用外部 DI 容器注入自定义 IStringLocalizer。

    默认的本地化实现只能支持放入 App_GlobalResources 文件夹的文件。请注意,这是problematic with MVC,因为添加这些文件时的默认设置会使它们以 MVC 无法访问的方式编译。我们目前是gathering requirements to make a new extension point,它允许从其他位置配置资源。

    【讨论】:

    • 感谢您的详细回答,它帮助很大,添加 PreservedRouteParameters 解决了我的问题。关于本地化,我不想从外部程序集本地化。我已经在 App_GlobalResources 文件夹中有资源文件。但是在 MvcSiteMapNode 构造函数中找了一个参数 TitleResourceKey 却看不到。如何让 MvcSiteMapNode 从 App_GlobalResources 文件中的 Resources.resx 文件中读取标题?
    • 我尝试了 Title="$resources:Resources.Resource,HomePage" 但得到“带有键 'Home' 的节点没有设置 'title'。标题是每个节点的必填字段。 "错误。编辑:我将代码更改为 Title="$resources:Resource,HomePage" 并工作。谢谢你的一切
    猜你喜欢
    • 2021-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-22
    • 1970-01-01
    • 2016-11-07
    • 1970-01-01
    相关资源
    最近更新 更多