c# - 如何使用多个子站点地图构建站点地图?

标签 c# asp.net-mvc-4 mvcsitemapprovider asp.net-mvc-sitemap

我正在使用 MVC4、MvcSiteMapProvider v3.2.1(需要升级到v4)。

我的问题是应用程序很大。我想模块化应用程序并使模块可插拔。
由于站点地图已经很大,我想使站点地图也 pluggalbe。
有没有一种方法可以在应用程序启动时使用根站点地图从多个 xml 文件加载节点来构建站点地图?

这里举例说明:
原始站点地图:

<?xml version="1.0" encoding="utf-8" ?>
<mvcSiteMap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-3.0"
            xsi:schemaLocation="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-3.0 MvcSiteMapSchema.xsd"
            enableLocalization="true">

  <mvcSiteMapNode title="Home" controller="Home" action="Index">

    <mvcSiteMapNode title="Staff List" controller="Staff" action="List">
       <mvcSiteMapNode title="Create Staff" controller="Staff" action="Create"/>
       <mvcSiteMapNode title="Edit Staff" controller="Staff" action="Edit"/>
       <mvcSiteMapNode title="View Staff" controller="Staff" action="Details">
           <mvcSiteMapNode >
           ...
       </mvcSiteMapNode>
    </mvcSiteMapNode>

    <mvcSiteMapNode title="Client List" controller="Client" action="List">
       <mvcSiteMapNode title="Create Client" controller="Client" action="Create"/>
       <mvcSiteMapNode title="Edit Client" controller="Client" action="Edit"/>
       <mvcSiteMapNode title="View Client" controller="Client" action="Details">
           <mvcSiteMapNode >
           ...
       </mvcSiteMapNode>
    </mvcSiteMapNode>
   </mvcSiteMapNode>
</mvcSiteMap>

我想将站点地图拆分为:
根站点地图:

  <mvcSiteMapNode title="Home" controller="Home" action="Index">

    <subsitemap file="StaffSiteMap">// something like this.

    <subsitemap file="ClientSiteMap">// something like this.
   </mvcSiteMapNode>
</mvcSiteMap>

员工站点地图:

<mvcSiteMapNode title="Staff List" controller="Staff" action="List">
  <mvcSiteMapNode title="Create Staff" controller="Staff" action="Create"/>
  <mvcSiteMapNode title="Edit Staff" controller="Staff" action="Edit"/>
  <mvcSiteMapNode title="View Staff" controller="Staff" action="Details">
    <mvcSiteMapNode />
    ...
  </mvcSiteMapNode>
</mvcSiteMapNode>

客户端站点地图:

<mvcSiteMapNode title="Client List" controller="Client" action="List">
  <mvcSiteMapNode title="Create Client" controller="Client" action="Create"/>
  <mvcSiteMapNode title="Edit Client" controller="Client" action="Edit"/>
  <mvcSiteMapNode title="View Client" controller="Client" action="Details">
    <mvcSiteMapNode >
      ...
    </mvcSiteMapNode>
  </mvcSiteMapNode>
</mvcSiteMapNode>

最佳答案

您的方法可能会导致您创建比您需要更多的节点。除非在搜索引擎中索引您的 CRUD 操作很重要,否则有一个捷径。您可以为“索引”、“创建”、“编辑”、“删除”、“详细信息”使用一组节点,并使用 preservedRouteParameters强制他们匹配所有可能的“id”。

首先,您需要正确嵌套节点,以便它们在每种情况下都显示正确的痕迹。

<mvcSiteMapNode title="Staff" controller="Staff" action="List">
    <mvcSiteMapNode title="Create New" action="Create" />
    <mvcSiteMapNode title="Details" action="Details" preservedRouteParameters="id">
        <mvcSiteMapNode title="Edit" action="Edit" preservedRouteParameters="id"/>
        <mvcSiteMapNode title="Delete" action="Delete" preservedRouteParameters="id"/>
    </mvcSiteMapNode>
</mvcSiteMapNode>

“编辑”、“删除”和“详细信息”节点都不会出现在菜单或其他控件中,因此您需要使用 FilteredSiteMapNodeVisibilityProvider在这种情况下使它们不可见。您可以将默认可见性提供程序设置为 FilteredSiteMapNodeVisibilityProvider在配置中,因此您不必在每个节点上都进行设置。您还应该设置 VisibilityAffectsDescendants属性设置为 false 以确保每个节点将始终打开和关闭,而不是在其父节点不可见时不可见。

内部 DI (web.config):

<appSettings>
    <add key="MvcSiteMapProvider_DefaultSiteMapNodeVisibiltyProvider" value="MvcSiteMapProvider.FilteredSiteMapNodeVisibilityProvider, MvcSiteMapProvider"/>
    <add key="MvcSiteMapProvider_VisibilityAffectsDescendants" value="false"/>
</appSettings>

外部 DI(在 DI 模块中,显示了 StructureMap 示例):

bool visibilityAffectsDescendants = false;

// Module code omitted here...

// Visibility Providers
this.For<ISiteMapNodeVisibilityProviderStrategy>().Use<SiteMapNodeVisibilityProviderStrategy>()
    .Ctor<string>("defaultProviderName").Is("MvcSiteMapProvider.FilteredSiteMapNodeVisibilityProvider, MvcSiteMapProvider");

要完成可见性,您需要设置 visibility每个节点的属性。

<mvcSiteMapNode title="Staff" controller="Staff" action="List">
    <mvcSiteMapNode title="Create New" action="Create" visibility="SiteMapPathHelper,!*" />
    <mvcSiteMapNode title="Details" action="Details" preservedRouteParameters="id" visibility="SiteMapPathHelper,!*">
        <mvcSiteMapNode title="Edit" action="Edit" preservedRouteParameters="id" visibility="SiteMapPathHelper,!*"/>
        <mvcSiteMapNode title="Delete" action="Delete" preservedRouteParameters="id" visibility="SiteMapPathHelper,!*"/>
    </mvcSiteMapNode>
</mvcSiteMapNode>

此外,您可能希望设置“索引”节点的标题,以便它显示当前记录的标题。你可以用 SiteMapTitleAttribute 做到这一点在您的每个操作方法中。

[SiteMapTitle("Name")]
public ActionResult Details(int id)
{
    using (var db = new EntityContext())
    {
        var model = (from staff in db.Staff
                    where staff.Id == id
                    select staff).FirstOrDefault();

        return View(model);
    }
}

假设您的 Staff 表中有一个名为“Name”的字段。您还需要在编辑和删除方法(get 和 post)中进行设置。但您还需要确保将属性目标设置为 ParentNode因此它将覆盖“详细信息”节点的标题。

[SiteMapTitle("Name", Target = AttributeTarget.ParentNode)]
public ActionResult Edit(int id)
{
    using (var db = new EntityContext())
    {
        var model = (from staff in db.Staff
                     where staff.Id == id
                     select staff).FirstOrDefault();
        return View(model);
    }
}

[HttpPost]
[SiteMapTitle("Name", Target = AttributeTarget.ParentNode)]
public ActionResult Edit(int id, Staff staff)
{
    try
    {
        using (var db = new EntityContext())
        {
            var model = (from s in db.Staff
                         where s.Id == id
                         select s).FirstOrDefault();
            if (model != null)
            {
                model.Name = staff.Name;

                db.SaveChanges();
            }
        }

        return RedirectToAction("Index");
    }
    catch
    {
        return View();
    }
}

结果是您将拥有根据选择的记录而变化的假面包屑。

Home > Staff 
Home > Staff > Create New 
Home > Staff > John Doe 
Home > Staff > John Doe > Edit 
Home > Staff > John Doe > Delete

有关可下载的工作演示,请参阅 Forcing-A-Match工程在代码下载How to Make MvcSiteMapProvider Remember a User's Position .

请注意,在 MvcSiteMapProvider 版本 3.x 中也可以执行此操作,您只需在 siteMap/providers/add 中设置默认可见性提供程序即可。标记并忽略关于 VisibilityAffectsDescendants 的部分.

<siteMap defaultProvider="MvcSiteMapProvider" enabled="true">
    <providers>
        <clear/>
        <add name="MvcSiteMapProvider"
             type="MvcSiteMapProvider.DefaultSiteMapProvider, MvcSiteMapProvider"
             siteMapFile="~/Mvc.Sitemap"
             securityTrimmingEnabled="true"
             cacheDuration="5"
             enableLocalization="true"
             scanAssembliesForSiteMapNodes="true"
             excludeAssembliesForScan=""
             includeAssembliesForScan=""
             attributesToIgnore="bling,visibility"
             nodeKeyGenerator="MvcSiteMapProvider.DefaultNodeKeyGenerator, MvcSiteMapProvider"
             controllerTypeResolver="MvcSiteMapProvider.DefaultControllerTypeResolver, MvcSiteMapProvider" 
             actionMethodParameterResolver="MvcSiteMapProvider.DefaultActionMethodParameterResolver, MvcSiteMapProvider"
             aclModule="MvcSiteMapProvider.DefaultAclModule, MvcSiteMapProvider"
             routeMethod=""
             siteMapNodeUrlResolver="MvcSiteMapProvider.DefaultSiteMapNodeUrlResolver, MvcSiteMapProvider"
             siteMapNodeVisibilityProvider="MvcSiteMapProvider.FilteredSiteMapNodeVisibilityProvider, MvcSiteMapProvider"
             siteMapProviderEventHandler="MvcSiteMapProvider.DefaultSiteMapProviderEventHandler, MvcSiteMapProvider"/>
    </providers>
</siteMap>

如果您仍然认为有必要将您的 SiteMap 组织成更小的文件,那么在 3.x 版本中这是不可能的。如果您使用外部 DI 并重复 XmlSiteMapNodeProvider,则在 4.x 版中可以在您的配置中使用多个 XML 文件。同一个 SiteMap 多次。以下是您如何使用 StructureMap 来做到这一点。

// Prepare for our node providers
var rootXmlSource = this.For<IXmlSource>().Use<FileXmlSource>()
   .Ctor<string>("fileName").Is(HostingEnvironment.MapPath("~/Root.sitemap"));
var staffXmlSource = this.For<IXmlSource>().Use<FileXmlSource>()
   .Ctor<string>("fileName").Is(HostingEnvironment.MapPath("~/Staff.sitemap"));
var clientXmlSource = this.For<IXmlSource>().Use<FileXmlSource>()
   .Ctor<string>("fileName").Is(HostingEnvironment.MapPath("~/Client.sitemap"));

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

请注意,每个 XML 文件仍需要一个根节点,但如果 includeRootNode 参数为 false,则不会将其解析到 SiteMap 中。实际上这与嵌套 Staff.sitemap 的节点相同和 Client.sitemap Root.sitemap主页下方的文件.

Root.sitemap

<?xml version="1.0" encoding="utf-8" ?>
<mvcSiteMap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-4.0"
    xsi:schemaLocation="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-4.0 MvcSiteMapSchema.xsd">

  <mvcSiteMapNode title="Home" controller="Home" action="Index" key="Home"/>
</mvcSiteMap>

Staff.sitemap

<?xml version="1.0" encoding="utf-8" ?>
<mvcSiteMap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-4.0"
    xsi:schemaLocation="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-4.0 MvcSiteMapSchema.xsd">

    <mvcSiteMapNode title="Home" controller="Home" action="Index" key="Home">

        <mvcSiteMapNode title="Staff List" controller="Staff" action="List">
           <mvcSiteMapNode title="Create Staff" controller="Staff" action="Create"/>
           <mvcSiteMapNode title="Edit Staff" controller="Staff" action="Edit"/>
           <mvcSiteMapNode title="View Staff" controller="Staff" action="Details">
               <mvcSiteMapNode >
               ...
           </mvcSiteMapNode>
        </mvcSiteMapNode>

   </mvcSiteMapNode>
</mvcSiteMap>

请注意,您需要确保根节点在每个 XML 文件中都具有相同的键 - 最好的方法是在每个文件中将键显式设置为相同的值。另请注意,对于 XML,不可能将不同文件中的节点附加到比主页节点更深的位置。尽管您可以在额外文件中将节点相互嵌套,但它们都必须附加到主页。

但是,如果您使用 IDynamicNodeProvider , ISiteMapNodeProvider , 或 [MvcSiteMapNode] attribute ,您可以在任何需要的地方嵌套每个提供者的节点。

关于c# - 如何使用多个子站点地图构建站点地图?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23140784/

相关文章:

c# - 是否有 WPF 复选框控件切换事件?

c# - 无法确定类型之间关联的主体端 - Entity Framework 错误,同一类之间的类关系

asp.net-mvc-4 - SimpleMembershipInitializer 不会初始化

c# - ASP.NET MVC 4 异步 Controller 回调

c# - 构造函数中的 ASP.NET Core 选项依赖项

c# - 对象到 URL 字符串

c# - mvcsitemapprovider - 从不同的页面导航到相同的 ActionResult

c# - 使用 MVCSiteMapPath 的样式化面包屑导航

asp.net-mvc - MvcSiteMapProvider 导致 MVC4 网站出现授权问题

c# - 如何比较两个对象列表?