在 MvcSiteMapProvider 中记住祖先
Remembering Ancestors in 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
假设让我到达 Policy
节点的 url 是 http://localhost:36695/AACOAgentPortal/details/Policy/814861364767412
。
一旦我向下导航到 County
节点,我的面包屑导航如下所示:
但是,如果我将鼠标悬停在 Policy
面包屑上,给出的 url 是 http://localhost:36695/AACOAgentPortal/Object/Details?typeName=Policy&typeFullName=AACOBusinessModel.AACO.HarvestMax.Policy
。如您所见,id
不见了。
示例 2:消失的标题
正如您在我的控制器中看到的那样,我告诉 mvc 站点地图我想使用标签 属性 来显示节点标题。当它是叶节点时它会这样做:
但是一旦我越过它,它就消失了:
这两个问题可能有一个共同的原因。我可能想要在面包屑路径中引用祖先还有其他原因,但这是两个具体的例子来说明这个问题。
使用 preservedRouteParameters
时,它检索的值的来源来自 当前请求 。因此,如果您希望在层次结构中向上导航,则不能将 id
重新用于其他目的。此外,您必须确保所有祖先 preservedRouteParameters
都包含在当前请求中,否则将无法正确构建 URL。
这里有一个如何使用 preservedRouteParameters
的演示:https://github.com/NightOwl888/MvcSiteMapProvider-Remember-User-Position。
我通过在会话中将我的对象保持在层次结构中来解决这个问题,并且每个对象都具有与其节点相同的键,以便它可以在处理每个请求时找到该节点。
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)
}
我通常需要在遍历站点地图时保留对我祖先的引用。
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
假设让我到达 Policy
节点的 url 是 http://localhost:36695/AACOAgentPortal/details/Policy/814861364767412
。
一旦我向下导航到 County
节点,我的面包屑导航如下所示:
但是,如果我将鼠标悬停在 Policy
面包屑上,给出的 url 是 http://localhost:36695/AACOAgentPortal/Object/Details?typeName=Policy&typeFullName=AACOBusinessModel.AACO.HarvestMax.Policy
。如您所见,id
不见了。
示例 2:消失的标题
正如您在我的控制器中看到的那样,我告诉 mvc 站点地图我想使用标签 属性 来显示节点标题。当它是叶节点时它会这样做:
但是一旦我越过它,它就消失了:
这两个问题可能有一个共同的原因。我可能想要在面包屑路径中引用祖先还有其他原因,但这是两个具体的例子来说明这个问题。
使用 preservedRouteParameters
时,它检索的值的来源来自 当前请求 。因此,如果您希望在层次结构中向上导航,则不能将 id
重新用于其他目的。此外,您必须确保所有祖先 preservedRouteParameters
都包含在当前请求中,否则将无法正确构建 URL。
这里有一个如何使用 preservedRouteParameters
的演示:https://github.com/NightOwl888/MvcSiteMapProvider-Remember-User-Position。
我通过在会话中将我的对象保持在层次结构中来解决这个问题,并且每个对象都具有与其节点相同的键,以便它可以在处理每个请求时找到该节点。
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)
}