2016-11-30 4 views
0

Ich habe eine allgemeine Notwendigkeit, einen Verweis auf meine Vorfahren beizubehalten, während ich die Sitemap durchlaufe.Erinnern an Vorfahren 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> 

-Controller

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

Es gibt viele Gründe, warum ich dies wollen, aber hier sind einige konkrete Beispiele.

Beispiel 1: Flucht IDs

Lassen Sie uns die URL sagen, die mir kam zu dem Policy Knoten http://localhost:36695/AACOAgentPortal/details/Policy/814861364767412 ist.

Sobald ich an, dass navigieren hinunter zum County Knoten, sieht meine Brotkrumen wie folgt aus: enter image description here

Allerdings, wenn ich über die Policy Brotkrümel schweben, da die URL http://localhost:36695/AACOAgentPortal/Object/Details?typeName=Policy&typeFullName=AACOBusinessModel.AACO.HarvestMax.Policy ist. Wie Sie sehen können, ist die id weg.

Beispiel 2: Flucht Titel

Wie Sie in meinem Controller sehen kann, ich sage mvc Sitemap, die ich die Label-Eigenschaft verwenden, um die Knotentitel angezeigt werden soll. Es tut das, wenn es der Blattknoten ist:

enter image description here

Aber sobald ich an, dass gehen, es verschwindet: enter image description here

Beide Probleme eine gemeinsame Ursache haben kann. Es gibt andere Gründe, warum ich einen Bezug zu einem Vorfahren entlang des Brotkrumenweges haben möchte, aber dies sind zwei konkrete, um das Problem zu veranschaulichen.

Antwort

0

Ich löse dies, indem ich meine Objekte in einer Hierarchie in der Sitzung belasse und jedes Objekt den gleichen Schlüssel hat wie der Knoten, so dass er den Knoten bei der Verarbeitung jeder Anfrage finden kann.

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}"; 
    } 
    } 
} 

Verwendung Beispiel

 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) 
     } 
0

Wenn preservedRouteParameters verwenden, die Quelle des Wertes ist es ruft aus der aktuellen Anforderung. Daher können Sie id nicht für einen anderen Zweck wiederverwenden, wenn Sie erwarten, in der Hierarchie nach oben zu navigieren. Außerdem müssen Sie sicherstellen, dass alle Vorfahren preservedRouteParameters in der aktuellen Anfrage enthalten sind oder die URLs nicht korrekt erstellt werden.

Es gibt eine Demo von preservedRouteParameters hier: https://github.com/NightOwl888/MvcSiteMapProvider-Remember-User-Position.

+0

Dank. Zu jedem Knoten gehört nur eine ID (und ein Objekt). Letztendlich habe ich das gelöst, indem ich eine Sammlung von 'MyNamespace.MenuItem'-Modellobjekten erstellt habe, die ich bei der Navigation aktualisiere, die ich in der Benutzersitzung ablege, und ich konsultiere nur diese Sammlung, wenn ich das diesem Knoten zugeordnete .net-Objekt erhalten möchte . Dies ist analog zur 'object Tag'-Eigenschaft auf dem alten winforms 'TreeView'-Steuerelement' TreeNode'. Ist mein Ansatz übertrieben? Macht der 'MvcSiteMapProvider' das schon? – toddmo

+0

Ja, das Standardverhalten (ohne Verwendung von preservedRouteParameters) erwartet, dass Sie 1 Knoten pro Entity erstellen, der dann beim Start zwischengespeichert und zwischen Benutzern geteilt wird. Der Sitzungsstatus wird wahrscheinlich weniger effizient sein. Sie können einen [Dynamic Node Provider] (https://github.com/maartenba/MvcSiteMapProvider/wiki/Defining-sitemap-nodes-using-IDynamicNodeProvider) verwenden, um alle Ihre Entitäten zu laden. – NightOwl888

Verwandte Themen