记住MvcSiteMapProvider

时间:2016-11-30 19:52:34

标签: mvcsitemapprovider

当我遍历站点地图时,我一般需要维护对祖先的引用。

Mvc.sitemap

<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" >
    <mvcSiteMapNode title="Products" url="~/Home/Products" roles="*">
      <mvcSiteMapNode title="Harvest MAX" url="~/Home/Products/HarvestMAX" >
        <mvcSiteMapNode title="Policies" url="~/Home/Products/HarvestMAX/Policy/List" productType="HarvestMax" type="P" typeFullName="AACOBusinessModel.AACO.HarvestMax.Policy" roles="*">
          <mvcSiteMapNode title="Policy" controller="Object" action="Details" typeName="Policy" typeFullName="AACOBusinessModel.AACO.HarvestMax.Policy" preservedRouteParameters="id" roles="*">
            <mvcSiteMapNode title="Counties" controller="Object" action="List" collection="Counties" roles="*">
              <mvcSiteMapNode title="County" controller="Object" action="Details" typeName="County" typeFullName="*" preservedRouteParameters="id" roles="*">
                <mvcSiteMapNode title="Land Units" controller="Object" action="List" collection="LandUnits" roles="*">
                  <mvcSiteMapNode title="Land Unit" controller="Object" action="Details" typeName="LandUnit" typeFullName="AACOBusinessModel.AACO.LandUnit" preservedRouteParameters="id" roles="*">
                  </mvcSiteMapNode>
                </mvcSiteMapNode>
              </mvcSiteMapNode>    
            </mvcSiteMapNode>
          </mvcSiteMapNode>
        </mvcSiteMapNode>
      </mvcSiteMapNode>
    </mvcSiteMapNode>
  </mvcSiteMapNode>
</mvcSiteMap>

控制器

[SiteMapTitle("Label")]
public ActionResult Details(string typeFullName, decimal id)
{
  return View(AACOBusinessModel.AACO.VersionedObject.GetObject( typeFullName?.ToType() ?? Startup.CurrentType,
                                                                ApplicationSignInManager.UserCredentials.SessionId,
                                                                id));
}

我想要这个有很多原因,但这里有一些具体的例子。

示例1:消失的ID&#39;

我们说让我进入Policy节点的网址为http://localhost:36695/AACOAgentPortal/details/Policy/814861364767412

一旦我向下导航到County节点,我的面包屑看起来像这样: enter image description here

但是,如果我将鼠标悬停在Policy面包屑上,则提供的网址为http://localhost:36695/AACOAgentPortal/Object/Details?typeName=Policy&typeFullName=AACOBusinessModel.AACO.HarvestMax.Policy。如您所见,id已消失。

示例2:消失的标题

正如您在我的控制器中看到的,我告诉mvc sitemap我想使用Label属性来显示节点标题。当它是叶子节点时它会这样做:

enter image description here

但是一旦我超越它,它就消失了: enter image description here

这两个问题都可能有共同的原因。还有其他原因我可能想要沿着痕迹路径引用祖先,但这些是用来举例说明这个问题的两个具体原因。

2 个答案:

答案 0 :(得分:0)

使用preservedRouteParameters时,它检索的值的来源来自 当前请求 。因此,如果您希望在层次结构中向上导航,则不能将id用于其他目的。此外,您必须确保当前请求中包含所有祖先preservedRouteParameters,否则将无法正确构建网址。

此处有一个如何使用preservedRouteParameters的演示:https://github.com/NightOwl888/MvcSiteMapProvider-Remember-User-Position

答案 1 :(得分:0)

我通过在会话中将对象保持在层次结构中来解决此问题,并且每个对象具有与其节点相同的密钥,以便在处理每个请求时可以找到该节点。

<强> MenuItems.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using AtlasKernelBusinessModel;
using MvcSiteMapProvider;
using CommonBusinessModel.Commands;
using CommonBusinessModel.Extensions;
using CommonBusinessModel.Security;

namespace AtlasMvcWebsite.Code
{
  [Serializable]
  public class MenuItems : Dictionary<string, MenuItem>
  {
    #region Properties

    public IEnumerable<Command> AvailableCommands
    {
      get
      {
        return CurrentItem?.Commandable?.AvailableCommands() ?? new List<Command>();
      }
    }

    /// <summary>
    /// Each User has his own copy because it has to track his travel through the hierarchy
    /// </summary>
    public static MenuItems Current
    {
      get
      {
        return (MenuItems)(HttpContext.Current.Session["MenuItems"] =
                            HttpContext.Current.Session["MenuItems"] ??
                            new MenuItems());
      }
    }

    private MenuItem currentItem;
    public MenuItem CurrentItem
    {
      get
      {
        return currentItem =
                CurrentNode == null ?
                null :
                this[CurrentNode.Key] =
                  ContainsKey(CurrentNode.Key) ?
                  this[CurrentNode.Key] :
                  new MenuItem(CurrentNode,
                               CurrentNode.ParentNode != null ? this[CurrentNode.ParentNode.Key] : null);
      }
    }

    public ISiteMapNode CurrentNode { get; private set; }

    #endregion

    #region Methods
    private void Build()
    {
      Build(SiteMaps.Current.RootNode);
    }

    private void Build(ISiteMapNode node)
    {
      foreach (var childNode in node.ChildNodes)
      {
        foreach (var att in node.Attributes.Where(a => !childNode.Attributes.Any(na => na.Key == a.Key)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value))
        {
          switch (att.Key)
          {
            case "productType":
            case "typeFullName":
            case "typeName":
              childNode.Attributes[att.Key] = att.Value;
              childNode.RouteValues[att.Key] = att.Value;
              break;
          }
        }
        Build(childNode);
      }
    }

    /// <summary>
    /// We finally have an object from the details controller and we want to set it to the current menu item
    /// </summary>
    /// <param name="versionedObject"></param>
    /// <returns></returns>
    public AtlasKernelBusinessModel.VersionedObject Set(AtlasKernelBusinessModel.VersionedObject versionedObject)
    {
      ((ICommandable)versionedObject).UserAccess = User.Credentials.BusinessObjectAccessFor(versionedObject.ObjectType());
      if (CurrentItem != null)
        this[CurrentItem.Node.Key].Object = versionedObject;
      //else
      // for commands
      //SiteMapNodeObjects[SiteMapNodeObjects.Last().Key] = versionedObject;
      return versionedObject;
    }

    public void Sync()
    {
      //Build();
      CurrentNode = SiteMaps.Current.CurrentNode;//cache value of current node
      Values.ToList().ForEach(m => m.Sync());
    }
    #endregion

  }


}

<强烈> MenuItem.cs

using AtlasKernelBusinessModel;
using MvcSiteMapProvider;
using System;
using CommonBusinessModel.Commands;


namespace AtlasMvcWebsite.Code
{
  [Serializable]
  public class MenuItem
  {
    #region Constructors
    public MenuItem(ISiteMapNode node, MenuItem parent)
    {
      Node = node;
      Parent = parent;
    }
    #endregion

    public ISiteMapNode Node;

    public readonly MenuItem Parent;

    private ICommandable commandable;
    public ICommandable Commandable
    {
      get
      {
        return commandable;
      }
    }

    private VersionedObject @object;
    public VersionedObject Object
    {
      get
      {
        return @object;
      }

      set
      {
        @object = value;
        Type = @object.GetType();
        commandable = (ICommandable)@object;

        Sync();
      }
    }

    public Type Type;

    public void Sync()
    {
      // sync the node to the object
      if (Object == null) return;
      Node = SiteMaps.Current.FindSiteMapNodeFromKey(Node.Key);
      Node.Title = Type.GetProperty("Label").GetValue(Object).ToString();
      Node.RouteValues["id"] = Object.Id;
    }

    public override string ToString()
    {
      return $"{Parent?.Node?.Title}.{Node.Title}";
    }
  }
}

使用示例

      var menuItem = MenuItems.Current.CurrentItem; // ensure current item exists
      if (menuItem != null)
      {
      <!-- CHILD ITEM MENU -->
        @Html.MvcSiteMap().Menu(menuItem.Node, true, false, 1)

      <!-- COMMAND BUTTONS -->
        if (!viewModel.ReadOnly)
        {
          @Html.DisplayFor(m => menuItem.Commandable.BusinessOperations.Commands)
        }