I'm trying to implement my own extension for HtmlHelper that will output a link in a similar fashion to ActionLink.
I know I can do this easily with TagBuilder for instance, but I'd like to take advantage of the routing system ability to construct outgoing urls as described by Scott Guthrie in this vintage article.
My application is centered around organizations. One user may create an organization and an organization may have multiple locations. The main action takes place within a current location. I consider an organization to be a tenant in my application and the organization id is sent via the url.
Here's the route configuration for the above:
routes.MapRoute(
name: "Tenant",
url: "Tenants/{tenantId}/{action}",
defaults: new
{
controller = "Organizations",
action = "Dashboard",
id = UrlParameter.Optional
}
);
routes.MapRoute(
name: "TenantLocation",
url: "Tenants/{tenantId}/Locations/{locationId}/{controller}/{action}/{id}",
defaults: new
{
controller = "Dashboard",
action = "Index",
id = UrlParameter.Optional
}
);
For route "Tenant", the controller is always Organizations. This part of the application takes care of administrative actions regarding an organization as a whole. The context here contains a tenant id.
For route "TenantLocation", the context shifts inside a location, so the context contains a tenant id and a location id.
Now, I'd like to create two extension methods for HtmlHelper called TenantActionLink and TenantLocationActionLink to generate such links as:
/Tenants/150/Dashboard
or
/Tenants/150/Locations/300/Team/Edit/1000
The part "/Tenants/150/Locations/300/" could be considered as a prefix to be prepended to a URL, the ids being extracted by an ActionFilterAttribute and stored as properties in a BaseController class.
As I mentioned, I could generate the links easily with TagBuilder, but if I change the routes later, I have to update all the calls of the tenant action links methods in all the views and controllers.
Any advice on how to tackle this? I'm using ASP.NET MVC 5 and .NET Framework 4.6.1
Thanks.
Update to answer Stephen Muecke's questions:
What have you tried so far?
I implemented the extensions like this (for simplicity I'm including only the code for TenantLocationActionLink, the other one is similar):
public static MvcHtmlString TenantLocationActionLink(this HtmlHelper helper,
int tenandId,
int locationId,
string linkText,
string actionName,
string controllerName,
object htmlAttributes = null)
{
var url = $"/Tenants/{tenandId}/Locations/{locationId}/{controllerName}/{actionName}";
var tagBuilder = new TagBuilder("a");
tagBuilder.InnerHtml = linkText;
tagBuilder.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
tagBuilder.MergeAttribute("href", url);
return new MvcHtmlString(tagBuilder.ToString(TagRenderMode.Normal));
}
and what problems are you having?
I can't figure out how to handle the routeValues. My implementation is somehow a stepdown from the more flexible implementation of ActionLink, especially since it doesn't take into account routeValues. How am I going to handle the situations when my route configuration will get more complex?
And what are you expecting your extension method to do that the inbuilt ActionLink() or RouteLink() don't?
Prepend the tenant and location ids as described above. How could I use ActionLink() or RouteLink() to achieve this?
There is already a RouteLink()
extension method that you can use for this. You pass it the link text and the name of the route definition, and an object (or a RouteValueDictionary
) for the parameters and it will generate the correct href
attribute. In your case, for the Tenant
route
@Html.RouteLink("...", "Tenant", new { tenantId = 150, action = "Dashboard" })
generates .../Tenants/150/Dashboard
,
and for the TenantLocation
@Html.RouteLink("...", "TenantLocation", new { tenantId = 150, locationId = 300, controller = "Team", action = "Edit", id = 1000 })
generates ../Tenants/150/Locations/300/Team/Edit/1000