EndpointHelpers is a Roslyn source generator that creates strongly-typed helpers for ASP.NET Core MVC controllers. It generates:
IUrlHelperhelpers with action methods per controller.LinkGeneratorhelpers withGet{Action}Pathmethods, includingHttpContextoverloads.- Redirect helpers for controller actions, so
RedirectToAction("Index")becomesRedirectToIndex(). - View helpers on controllers, so
return View("Index")becomesreturn IndexView(). - Extension properties on
IUrlHelperandLinkGeneratorto access the helpers. - Attribute types are used to control generation.
This package ships only a source generator and generated code. There is no runtime dependency.
<a href='@Url.Action(action: "Details",controller: "Orders", values: new { orderId = 123, source = "dashboard" } )'> View order </a> <a href='@Url.Orders.Details(123, "dashboard")'> View order </a> Add the NuGet package:
<ItemGroup> <PackageReference Include="EndpointHelpers" Version="2.0.1"/> </ItemGroup>or
dotnet add package EndpointHelpersOptional (for better IntelliSense in JetBrains IDEs):
<ItemGroup> <PackageReference Include="JetBrains.Annotations" Version="2025.2.4" /> </ItemGroup>When JetBrains.Annotations is installed, generated MVC helper methods include ASP MVC annotations, and IntelliSense/navigation for views and model parameters works correctly when using R# or Rider.
Enable generation at the assembly level:
using EndpointHelpers; [assembly: GenerateUrlHelper] [assembly: GenerateLinkGenerator]Or apply to a specific controller:
using EndpointHelpers; [GenerateUrlHelper] [GenerateLinkGenerator] [GenerateRedirectToAction] [GenerateViewHelpers] public partial class HomeController : Controller { public IActionResult Index() => View(); public IActionResult Privacy() => View(); }Or only to a specific action:
using EndpointHelpers; public partial class HomeController : Controller { [GenerateUrlHelper] [GenerateRedirectToAction] public IActionResult Index() => View(); public IActionResult Privacy() => View(); }Redirect example:
public partial class OrdersController : Controller { public IActionResult Index() => View(); [GenerateRedirectToAction] public IActionResult Details(int orderId, string? source) => View(); public IActionResult Save() { return RedirectToDetails(orderId: 123, source: "created"); } }Generation can be enabled at different scopes:
- Assembly:
[assembly: GenerateUrlHelper],[assembly: GenerateLinkGenerator]. - Controller:
[GenerateUrlHelper],[GenerateLinkGenerator],[GenerateRedirectToAction]on the controller class. - Action:
[GenerateUrlHelper],[GenerateLinkGenerator],[GenerateRedirectToAction]on a specific action method. - View helpers:
[GenerateViewHelpers]on the controller class (controller-only, not per-action or assembly). GenerateRedirectToActiondoes not support assembly-level attributes and requires controllers declaredpartial.GenerateViewHelpersdoes not support assembly-level attributes and requires controllers declaredpartial.
You can exclude methods using:
[UrlHelperIgnore][LinkGeneratorIgnore][RedirectToActionIgnore][NonAction](standard ASP.NET Core MVC attribute)
- Controllers are discovered by name: non-abstract classes ending with
Controller. - Only public, ordinary methods are included.
- UrlHelper and LinkGenerator helpers are placed in the
EndpointHelpersnamespace. - Redirect helpers are generated as
partialmembers in the controller namespace. - Extension properties use the controller name without the
Controllersuffix. - Controllers with
[Area("...")]are grouped by area, so generated access becomesUrl.<Area>.<Controller>andLinks.<Area>.<Controller>.
[GenerateUrlHelper] [GenerateLinkGenerator] [GenerateRedirectToAction] public partial class OrdersController : Controller { public IActionResult Index() => View(); public IActionResult Details(int orderId, string? source) => View(); }Caching controller helpers avoid repeated allocations. This is a micro-optimization and usually not necessary in most scenarios. It can make sense in hot views where you call the same helper many times (for example, in a loop), but the savings are typically negligible compared to database access, rendering, or network costs. If you don’t measure a problem, skip this.
@{ var ordersUrlHelper = Url.Orders; } @for (var i = 0; i < Model.Orders.Count; i++) { var order = Model.Orders[i]; <a class="btn btn-sm btn-outline-secondary" href="@ordersUrlHelper.Details(order.Id)">Details</a> } // UrlHelperGenerator Url.Orders.Index(); Url.Orders.Details(orderId: 123, source: "dashboard"); Url.Admin.Users.Index(); // LinkGeneratorGenerator LinkGenerator.Orders.GetIndexPath(); LinkGenerator.Orders.GetDetailsPath(123, "dashboard"); LinkGenerator.Admin.Users.GetIndexPath(); // RedirectToActionGenerator RedirectToIndex(); RedirectToDetails(orderId: 123, source: "dashboard"); RedirectToUsers();See example/EndpointHelpers.Sample for a minimal MVC app using all generators.
GNU General Public License