You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This thread is locked to avoid spam for subscribers to the announcements repo. Please use aspnet/Mvc#3665 for questions and discussion.
Simplifying MVC Link Generation - Guidance for Routing with Areas
We're making a change in RC2 to remove a feature from Routing/MVC that I like to call magic link generation. This was added early-on as a companion to some changes in URL matching for MVC 6 with the hope that link generation would do the right thing in more cases.
In actuality magic link generation has garnered a "you moved my cheese" reaction more frequently than it has received praise. We're going to remove it and restore the MVC5 behavior for link generation.
Magic link generation only affects conventional routing (attribute routing doesn't need this feature) and only in cases where the action being linked to doesn't exist.
Description
Magic link generation prevents a route from generating a link, when the URL that would be generated doesn't match anything.
In MVC 6 beta-RC1
For instance, a route to the blog area /blog/{controller}/{action}/{id?} with values new { controller = "Products", "Index" } won't generate a link, unless there is a ProductsController with an Index() action in the blog area.
In MVC 6 RC2 & MVC1-5
The above example will generate the URL /blog/Products/Index without any consideration for whether or not this URL could be matched by a route.
Impact
Removal of this feature should only really affect your applications if you're defining routes to areas in a way that the less-specific (for link generation) routes come first. If you're using areas, update your routing definitions to look like one of the examples in the next section.
We'll be removing RoutingOptions.UseBestEffortLinkGeneration in a follow-up change. This was an opt-out for magic link generation behavior. If you're currently using RoutingOptions.UseBestEffortLinkGeneration == true then this change will likely have no impact on your application.
Guidance for using areas with conventional routes
If you're using conventional routing with areas, we recommend a few supported ways of authoring routes.
New in MVC6, you can use {area:...} as a token in route templates if url space is uniform across all areas. This is probably the least complicated option.
The only consideration is to put the route with {area:...} first. The reason why is this case:
@Url.Action("Index", "Home", new { area = "Admin" }); // Generates /Admin/Home/Index
The only thing stopping the {controller}/{action}/{id?} route from generating a link is ordering. Otherwise it would generate /Home/Index?area=Admin, which is not what you want.
This should look more familiar to how things worked in MVC5. The new MapAreaRoute extension method adds the provided area name (users, blog) as both a constraint and default to the routes, ensuring that they only generate a link when asked to.
Again, the routes that handle areas should come first a non-area route has the potential to generate a link that would be incorrect when trying to link to an action in an area.
The History
Read on only if you want an MVC history lesson
The problem that magic link generation solves is generating links to areas - there's no ordering of these routes that produces the correct link generation behavior. Unfortunately these routes are what an existing MVC5 user might have in their site.
Example 1:
app.UseMvc(routes =>
{
routes.MapRoute("Admin/{controller}/{action}/{id?}", defaults: new { area = "Admin" });
routes.MapRoute("{controller}/{action}/{id?}");
}
@Url.Action("Index", "Home"); // Generates /Admin/Home/Index
@Url.Action("Index", "Home", new { area = "Admin" }); // Generates /Admin/Home/Index
Example 2:
app.UseMvc(routes =>
{
routes.MapRoute("{controller}/{action}/{id?}");
routes.MapRoute("Admin/{controller}/{action}/{id?}", defaults: new { area = "Admin" });
}
@Url.Action("Index", "Home"); // Generates /Home/Index
@Url.Action("Index", "Home", new { area = "Admin" }); // Generates /Home/Index?area=Admin
In either of these cases, this is not desirable.. You can't use ordering to resolve the problem, because there's no ordering of routes that puts the 'most specific' first.
The lessons of history
MVC2-5 solved this problem by manually filtering the route collection. MVC2-5's link generation code had special knowledge of the area token (like it does controller and action), and would create a new route collection for each link generation operation containing just the routes that match.
While semantically correct, this solution was unacceptable from a performance point-of-view. Large sites like Orchard had to implement complicated workarounds to avoid this code path running. Since the route collection was part of System.Web and could change at any time (it was thread-safe) there wasn't much we could do to mitigate this problem.
Additionally, this solution has too much coupling to the concept of 'area' at multiple levels of the system. Routes in ASP.NET 5 are not introspectable like they were in ASP.NET 4, so we couldn't write this code even if we wanted to. In MVC6 we want to support arbitrary hierarchies of action-selection structure (like subareas) so we needed to find a way to support this behavior without hardcoding area into the link generation code.
Arriving at the MVC6 solution
We whiteboarded a lot of examples of using routing for areas and for 'static' routes directly to a controller or action. In the end what made sense was to rely on existing routing features rather than add something new. Routing is already very complex.
The new MapAreaRoute is implemented very simply to add a route with a default and constraint so that it only generates a URL when an area value is provided.
routes.MapRoute(
"admin_Route",
"Admin/{controller}/{action}/{id?}",
defaults: new { area = "Admin" },
constraints: new { area = "Admin" });
The text was updated successfully, but these errors were encountered:
Uh oh!
There was an error while loading. Please reload this page.
This thread is locked to avoid spam for subscribers to the announcements repo. Please use aspnet/Mvc#3665 for questions and discussion.
Simplifying MVC Link Generation - Guidance for Routing with Areas
We're making a change in RC2 to remove a feature from Routing/MVC that I like to call magic link generation. This was added early-on as a companion to some changes in URL matching for MVC 6 with the hope that link generation would do the right thing in more cases.
In actuality magic link generation has garnered a "you moved my cheese" reaction more frequently than it has received praise. We're going to remove it and restore the MVC5 behavior for link generation.
Magic link generation only affects conventional routing (attribute routing doesn't need this feature) and only in cases where the action being linked to doesn't exist.
Description
Magic link generation prevents a route from generating a link, when the URL that would be generated doesn't match anything.
In MVC 6 beta-RC1
For instance, a route to the
blog
area/blog/{controller}/{action}/{id?}
with valuesnew { controller = "Products", "Index" }
won't generate a link, unless there is aProductsController
with anIndex()
action in theblog
area.In MVC 6 RC2 & MVC1-5
The above example will generate the URL
/blog/Products/Index
without any consideration for whether or not this URL could be matched by a route.Impact
Removal of this feature should only really affect your applications if you're defining routes to areas in a way that the less-specific (for link generation) routes come first. If you're using areas, update your routing definitions to look like one of the examples in the next section.
We'll be removing
RoutingOptions.UseBestEffortLinkGeneration
in a follow-up change. This was an opt-out for magic link generation behavior. If you're currently usingRoutingOptions.UseBestEffortLinkGeneration == true
then this change will likely have no impact on your application.Guidance for using areas with conventional routes
If you're using conventional routing with areas, we recommend a few supported ways of authoring routes.
Example 1 - using
{area:exists}
in the template:New in MVC6, you can use
{area:...}
as a token in route templates if url space is uniform across all areas. This is probably the least complicated option.The only consideration is to put the route with
{area:...}
first. The reason why is this case:The only thing stopping the
{controller}/{action}/{id?}
route from generating a link is ordering. Otherwise it would generate/Home/Index?area=Admin
, which is not what you want.Example 2 - individual routes for each area:
This should look more familiar to how things worked in MVC5. The new
MapAreaRoute
extension method adds the provided area name (users
,blog
) as both a constraint and default to the routes, ensuring that they only generate a link when asked to.Again, the routes that handle areas should come first a non-area route has the potential to generate a link that would be incorrect when trying to link to an action in an area.
The History
Read on only if you want an MVC history lesson
The problem that magic link generation solves is generating links to areas - there's no ordering of these routes that produces the correct link generation behavior. Unfortunately these routes are what an existing MVC5 user might have in their site.
Example 1:
Example 2:
In either of these cases, this is not desirable.. You can't use ordering to resolve the problem, because there's no ordering of routes that puts the 'most specific' first.
The lessons of history
MVC2-5 solved this problem by manually filtering the route collection. MVC2-5's link generation code had special knowledge of the area token (like it does controller and action), and would create a new route collection for each link generation operation containing just the routes that match.
While semantically correct, this solution was unacceptable from a performance point-of-view. Large sites like Orchard had to implement complicated workarounds to avoid this code path running. Since the route collection was part of
System.Web
and could change at any time (it was thread-safe) there wasn't much we could do to mitigate this problem.Additionally, this solution has too much coupling to the concept of 'area' at multiple levels of the system. Routes in ASP.NET 5 are not introspectable like they were in ASP.NET 4, so we couldn't write this code even if we wanted to. In MVC6 we want to support arbitrary hierarchies of action-selection structure (like subareas) so we needed to find a way to support this behavior without hardcoding
area
into the link generation code.Arriving at the MVC6 solution
We whiteboarded a lot of examples of using routing for areas and for 'static' routes directly to a controller or action. In the end what made sense was to rely on existing routing features rather than add something new. Routing is already very complex.
The new
MapAreaRoute
is implemented very simply to add a route with a default and constraint so that it only generates a URL when an area value is provided.The text was updated successfully, but these errors were encountered: