maartenba / MvcSiteMapProvider

An ASP.NET MVC SiteMapProvider implementation for the ASP.NET MVC framework.
Microsoft Public License
537 stars 220 forks source link

Generic Map without parameters #406

Open NBONDEZANMERRION opened 8 years ago

NBONDEZANMERRION commented 8 years ago

I am studying the MVCSiteMapProvider to use it in my company project, and we decided to use the Dynamic Node approach to build the map. We have a table with our Areas, Controller and Actions mapped to give the permissions per user, I would like to use this same table to populate my Dynamic Nodes, the problem is when I am mapping an action that need parameters. I found two solutions for this situation: 1 - Generate an new Node for each record in my database 2 - Use the "PreserverRouteParameter" property

But in this second option I need to know the name of my parameter, and I don't have this information in my database.

There is no way to map a action and just ignore the parameters?

fcsobel commented 8 years ago

We ran into a similar issue where we wanted to match on controller & action without using preserverRouteParameters. Allowing parameters to be ignored would be very usefull.

Our work around was to add an action filter to dynamically inject all parameters into the RouteValues for the node. This forces mvcSiteMapProvider to match without preserverRouteParameters. To make it work you associate the filter with your actions and set key="{controller}.{action}" for the nodes so the action filter can find them and stuff the route values.

You can see the code for the MvcSiteMapIgnoreParameters filter on issues/404

Note: If you are using area modify the code and key to: {area}.{controller}.{action}

NightOwl888 commented 8 years ago

@NBONDEZANMERRION

I am not sure how you would expect that to work. The 2 methods you mention are equivalent to a 1-1 relationship and a 1-many relationship.

<mvcSiteMapNode title="Product 1" controller="Product" action="Details" id="1"/>
<mvcSiteMapNode title="Product 2" controller="Product" action="Details" id="2"/>
<mvcSiteMapNode title="Product 3" controller="Product" action="Details" id="3"/>

The default behavior matches by a complete set of route values. The relationship is 1-1. This ensures the combination can only match 1 node and skip all of the other nodes that don't match. It also ensures you can get a match regardless of how many parameters or how complex the configuration.

<mvcSiteMapNode title="Product 1" controller="Product" action="Details" preservedRouteParameters="id"/>

The effect of using preservedRouteParamters overrides the default behavior. This creates a 1-many relationship, effectively matching any id (and preserving it so it will be used to construct the URL of the matching node and any ancestor nodes). It is effectively the same as this (and I have been considering adding this alternative syntax because it is easier to understand).

<mvcSiteMapNode title="Product 1" controller="Product" action="Details" id="*"/>

What you are proposing, would effectively be:

<mvcSiteMapNode title="Product 1" controller="Product" action="Details" *="*"/>

But that would always match the first node regardless of what additional parameters are on it, since the first match (not necessarily the best match) always wins.

You say that "those keys are not in your database", but that is not typically the case in any MVC application. Generally speaking you would configure the route keys into the node, much as you would with your keys in .NET routing and they would typically be either XML or .NET attribute based nodes, since using dynamic nodes would not be necessary in this case (although it is certainly possible).

If you were using dynamic node providers, you could just create a separate provider for each database table and hard-code the preserved route parameters (route keys) much as you would in the XML or .NET attribute configuration (or your .NET routing configuration for that matter), since a typical design treats all of the records in a table similarly.

  1. Just what information is in your database and how is your routing configured so that MVC can match a URL to a database record in your case?
  2. Just how many different route values could there be in your configuration that it would be impractical to specify them explicitly?
  3. How did you get around that in your routing, since it typically requires you to specify route keys explicitly?
  4. How did you get your route configuration to work with MVC (MvcSiteMapProvider node URLs work almost exactly the same way that ActionLink works)?

Do note that if you specify a key in preservedRouteParameters that doesn't happen to exist in the URL, it will simply not be considered as part of the match. So, even if you have 15 different parameters that may or may not exist in the URL, it wouldn't be such a big deal to specify them all, which would effectively "ignore" all 15 and carry the existing ones over so when your link URLs are generated they will exist there as well.

Also note that if you are using query strings, they are always ignored by default.

Furthermore, if you are using URL slugs in your database, you can always configure your nodes with URLs instead of with route values, and then you don't need an explicit route configuration in MvcSiteMapProvider. But I would definitely recommend using dynamic node providers (or ISiteMapNodeProvider) to load your URLs from the database if you go this route.

<mvcSiteMapNode title="Product 1" url="~/product-1" />

We have a table with our Areas, Controller and Actions mapped to give the permissions per user, I would like to use this same table to populate my Dynamic Nodes, the problem is when I am mapping an action that need parameters.

The recommended approach is to always load all nodes for all users, since caching will prevent anything from being truly dynamic (the name for "dynamic node provider" was a poor design choice), and then you can use one of the following approaches from these answers to show the nodes that correspond to the current user.

http://stackoverflow.com/questions/26541338/load-an-xml-sitemap-into-mvcsitemapprovider-based-on-user-role http://stackoverflow.com/questions/19625384/how-to-render-specific-sitemap-section-using-mvcsitemapprovider/19666647#19666647