Open git4sastra opened 8 years ago
I am unable to reproduce the behavior you describe. Here is a project demonstrating that the route
attribute works exactly the same way that RouteLink
works in an Area.
The behavior of the route attribute is meant to be exactly the same as using Html.RouteLink
or Url.RouteUrl
in MVC. The route name acts as a filter. It makes the routing framework compare only a single route with the supplied route values rather than every route in the route table. However, all of the required route values must still be supplied to get a match (that is, any non-optional values that don't have defaults set), or you will get a null
result. In a non-matching scenario in MvcSiteMapProvider
, you will instead get a #
, which effectively makes the current page link to itself (see #115 for an explanation).
It is possible that your routing is misconfigured. See this question as well as this question for some common routing pitfalls.
Ok, first wouldn't it be a better idea to use the route attribute to takeover the way the route is configured?!
Second, I think I know where the issue is ....
My configured routes looks like:
context.MapRoute( "Crm_Contacts_List", "Crm/Contacts/List/{group}/{sort}", new { controller = "Contacts", action = "Index", group = "Alle", sort = UrlParameter.Optional }, new { sort = "^Company_Asc$|^Company_Desc$|^Lastname_Asc$|^Lastname_Desc$" } );
And I tried the following XML configuration (also tried with preserved Parameters group and/or sort):
<mvcSiteMapNode title="$resources:Sitemap,Contacts_Title" route="Crm_Contacts_List" controller="Contacts" action="Index" visibility="CrmMenu,CrmBreadcrumb,!*" />
Is it possible that the fixed name "List" is an issue here?
Ok, first wouldn't it be a better idea to use the route attribute to takeover the way the route is configured?!
MvcSiteMapProvider
doesn't configure routing for you. It consumes your existing route configuration. Routing happens near the beginning of the ASP.NET lifecycle, and displaying the view (where MvcSiteMapProvider
comes into play) happens near the end of the lifecycle.
Perhaps in a future version, it might be possible to extend MVC routing to add the parent-child relationship and title that routing is missing in order to configure MvcSiteMapProvider
there. But for now, this is the way it works. Most people would argue that something that deals with the UI such as MvcSiteMapProvider
should be a completely separate concern than routing.
Is it possible that the fixed name "List" is an issue here?
On the contrary. Adding fixed values to the URL is one of the ways to make a route unique so routing works correctly. These fixed values affect the matching of the incoming URL and the building of the outgoing URL only. They do not affect the route values that are generated by the route (which are what MvcSiteMapProvider
uses).
I believe one problem is that you are putting a constraint on an optional value. I tried doing that before, but was unable to make it function and I am pretty sure routing is not designed to work that way.
An alternative way to configure it would be to make 2 different routes to replace your optional sort
value - one with the constraint and one without. In the first case, the sort
parameter is required and in the second case, it is not part of the URL at all.
Do note that making sort
required means that everything to its left is also required. Therefore, it makes no sense to have an optional group
in the first route.
context.MapRoute(
"Crm_Contacts_List_Sort",
"Crm/Contacts/List/{group}/{sort}",
new { controller = "Contacts", action = "Index" },
new { sort = "^Company_Asc$|^Company_Desc$|^Lastname_Asc$|^Lastname_Desc$" }
);
context.MapRoute(
"Crm_Contacts_List",
"Crm/Contacts/List/{group}",
new { controller = "Contacts", action = "Index", group = "Alle" }
);
I am not sure if this is exactly right for your use case, though. If it doesn't make sense to have a "default" group at all, then you should make it required by not supplying a default.
context.MapRoute(
"Crm_Contacts_List_Sort",
"Crm/Contacts/List/{group}/{sort}",
new { controller = "Contacts", action = "Index" },
new { sort = "^Company_Asc$|^Company_Desc$|^Lastname_Asc$|^Lastname_Desc$" }
);
context.MapRoute(
"Crm_Contacts_List",
"Crm/Contacts/List/{group}",
new { controller = "Contacts", action = "Index" }
);
Usually, the right way to configure routing is to use required URL parameters, not optional parameters. The typical case is to make 1 URL map to an action. Using optional parameters makes multiple URLs map to an action (unless the alternates are explicitly suppressed using IgnoreRoute
), which is neither SEO-freindly or intuitive.
In my first example, Crm_Contacts_List
matches both of these URLs:
/Crm/Contacts/List
/Crm/Contacts/List/Alle
In the second example, it only matches /CrmContacts/List/Alle
.
As for your node configuration, it is not matching in this case because the node is missing matching route values for group
and sort
. One way fix it would be to use preservedRouteParameters
:
<mvcSiteMapNode title="$resources:Sitemap,Contacts_Title" area="Crm" controller="Contacts" action="Index" preservedRouteParameters="group,sort" visibility="CrmMenu,CrmBreadcrumb,!*" />
If group
is an ambient value, that may work for you. Do note that since there is no longer a single route that this node can match, you must not specify it explicitly. When you do that, you need to specify the area
to ensure the route will match.
If group
is something that identifies the page rather than an ambient value, you should instead make a node for each group (typically using a dynamic node provider).
<mvcSiteMapNode title="$resources:Sitemap,Contacts_Title" area="Crm" controller="Contacts" action="Index" group="Group1" preservedRouteParameters="sort" visibility="CrmMenu,CrmBreadcrumb,!*" />
<mvcSiteMapNode title="$resources:Sitemap,Contacts_Title" area="Crm" controller="Contacts" action="Index" group="Group2" preservedRouteParameters="sort" visibility="CrmMenu,CrmBreadcrumb,!*" />
<mvcSiteMapNode title="$resources:Sitemap,Contacts_Title" area="Crm" controller="Contacts" action="Index" group="Group3" preservedRouteParameters="sort" visibility="CrmMenu,CrmBreadcrumb,!*" />
See How to Make MvcSiteMapProvider Remember a User's Position for a detailed explanation of these options and how they work.
First of all - thank you very much for your detailed explanation which gets me a little bit further. Maybe you didn't see my note, but I already tried preservedRouteParameters Nevertheless, I have now an approach that at least is generating a working link, but it adds the URL parameter area to the link, which still is not very nice. It doesn't even matter if I set the area attribute or not, I get the same result. Also I get an error if I set group as preservedRouteParameter stating that group is set in route parameters and preservedRouteParameters.
My configuration now looks like the following:
context.MapRoute(
"Crm_Contacts_List_Sorted",
"Crm/Contacts/List/{group}/{sort}",
new { controller = "Contacts", action = "Index", group = "", sort = UrlParameter.Optional },
new { sort = "^Company_Asc$|^Company_Desc$|^Lastname_Asc$|^Lastname_Desc$" }
);
context.MapRoute(
"Crm_Contacts_List",
"Crm/Contacts/List/{group}",
new { controller = "Contacts", action = "Index", group = "" }
);
and the XML configuration like:
<mvcSiteMapNode title="$resources:Sitemap,Contacts_Title" route="Crm_Contacts_List" controller="Contacts" action="Index" visibility="CrmMenu,CrmBreadcrumb,!*" />
The resulting link looks like the following:
/Crm/Contacts/List?area=Crm
If I now can get rid of the URL parameter area it would be perfect suited.
When using the route attribute in XML configuration for a defined area route it seems to be ignored. Is that by design and would I need to use an external DI or is it a bug?