-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Description
The Issue
The example project from the Razor Pages with Entity Framework Core in ASP.NET Core - Tutorial includes the following code:
<a asp-page="./Index"
asp-route-sortOrder="@Model.CurrentSort"
asp-route-pageIndex="@(Model.Students.PageIndex + 1)"
asp-route-currentFilter="@Model.CurrentFilter"
class="btn btn-primary @nextDisabled">
Next
</a>Link to those lines in the project.
The route parameter names:
asp-route-sortOrder="@Model.CurrentSort"
asp-route-pageIndex="@(Model.Students.PageIndex + 1)"
asp-route-currentFilter="@Model.CurrentFilter"
are not strongly-typed. I.e. there could be a typo in any of the names (sortOrder, pageIndex, currentFilter) and the project will compile and run however the program will not function as expected.
Workaround - use LinkGenerator.GetPathByAction
This stackoverflow post discusses one workaround for the issue. The example there has the following a tag-helper:
<a class="btn btn-block
@(category == ViewBag.SelectedCategory
? "btn-primary": "btn-outline-secondary")"
asp-action="Index" asp-controller="Home"
asp-route-category="@category"
asp-route-productPage="1">
@category
</a>It is rewritten in a more strongly-typed manner using LinkGenerator.GetPathByAction as follows:
<a class="btn btn-block @(category == ViewBag.SelectedCategory ? "btn-primary" : "btn-outline-secondary")"
href="@(
_linkGenerator.GetPathByAction(
nameof(HomeController.Index),
Regex.Replace(nameof(HomeController), "Controller$", String.Empty),
new IndexParameters()
{
Category = category,
ProductPage = 1
}))">
@category
</a>This is more of a demonstration of what is theoretically possible in the form of a quick solution; it's not ideal for end users.
Workaround - R4MVC
Another approach is to use R4MVC.
Here's a video demonstrating R4MVC.
Downsides to R4MVC:
- Still in pre-release
- Not built-in to ASP.NET Core
Workaround - object for parameters
If we use a class to represent the parameters to OnGetAsync:
public class OnGetAsyncParameters
{
public SortOrder? SortOrder { get; set; }
public string CurrentFilter { get; set; }
public string SearchString { get; set; }
public int? PageIndex { get; set; }
}then instead of:
<a asp-page="./Index"
asp-route-sortOrder="@Model.CurrentSort"
asp-route-pageIndex="@(Model.Students.PageIndex + 1)"
asp-route-currentFilter="@Model.CurrentFilter"
class="btn btn-primary @nextDisabled">
Next
</a>we can use asp-all-route-data and generate a dictionary from an instance of OnGetAsyncParameters:
<a asp-page="./Index"
asp-all-route-data="to_dict(new OnGetAsyncParameters()
{
SortOrder = Model.CurrentSort,
PageIndex = Model.Students.PageIndex + 1,
CurrentFilter = Model.CurrentFilter
})"
class="btn btn-primary @nextDisabled">
Next
</a>where to_dict is the following utility function:
Dictionary<string, string> to_dict(object obj)
{
return obj
.GetType()
.GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public)
.ToDictionary(
prop => prop.Name,
prop =>
{
var val = prop.GetValue(obj, null);
return val == null ? "" : val.ToString();
});
}In this case, OnGetAsync is updated to accept OnGetAsyncParameters:
public async Task OnGetAsync(OnGetAsyncParameters onGetAsyncParameters)Request
The request is for ASP.NET Core to have route parameters be more strongly-typed in a tag-helpers.
I understand that the priority may be low. I'd just like to request that it at least be on the roadmap. :-)