I'm working with ASP.NET Core 8.0.8. My razor page application has a dynamic Table.cshtml
page with route @page "/table/{table}"
that can take any model
and display the associated database information in a searchable, filterable, and sortable table. This Table.cshtml
page also shows the associated actions available to the user based on the model
. I have a TableModel.cs
class which handles all the base OnGet
and OnPost
requests but certain models will extend this class to include specific OnPost
handlers (OnPostCreate
, OnPostUpdate
, OnPostDelete
, etc...) or set different [Authorize(Roles = ...)]
roles. This is where my problem lies. I just don't know of a way to attach these derived TableModel
classes to my Table.cshtml
page without creating a separate/duplicate page for each. I have numerous models so I would really prefer to keep the rendering and base logic on one page.
For example:
/table/products
- Accessible to everyone, uses class TableModel.cs
/table/items
- Accessible to authenticated users, uses class ItemsTableModel.cs
/table/users
- Accessible to admins, uses class UsersTableModel.cs
I tried to manually set the page routing in Program.cs
but to no avail.
options.Conventions.AddPageRouteModelConvention("/table/items", model => { new MyApp.Pages.ItemsTableModel(builder.Configuration); });
options.Conventions.AddPageRouteModelConvention("/table/users", model => { new MyApp.Pages.UsersTableModel(builder.Configuration); });
I also tried using partial views but ran into trouble with the needed IConfiguration
.
I know setting the derived class as a property is also an option but I'm not sure how that would then work with the specific OnPost
handlers. Open to any suggestions (if this is even possible).
Here's how I was able to accomplish this:
Switched from an ASP.NET Core Web App (Razor Pages)
to an ASP.Net Core Web App (Model-View-Controller)
which made my solution possible.
Kept the default controller route mapping in my Program.cs
file:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}"
);
TableController.cs
class to my Controllers
that I would then extend with my specific table classes. The specific action calls can then be overwritten by the children classes as needed. I also have a ModelTable.cs
class that stores the data associated with each table:public class TableController : Controller {
public ModelTable Model { get; set; } = new ModelTable("");
[HttpGet]
public virtual IActionResult Index() {
return View("~/Views/Table.cshtml", Model);
}
[HttpPost]
public virtual IActionResult Index(string? returnURL) {
return Redirect(returnURL ?? $"/{Model.TableName}");
}
}
Users
), will be the URL route. So /Users
will call this controller:public class UsersController : TableController {
public UsersController() : base() {
Model = new UsersTable();
}
[HttpGet]
public override IActionResult Index() {
// Users-specific database load code here
return base.Index();
}
}
Table.cshtml
view will then use the provided and populated ModelTable.cs
class to render out the appropriate data in an HTML table:@model ModelTable
<table id="@Model.TableName">
@foreach (var column in Model.TableColumns) { ... }
</table>
*. For those who may stumble across this and ask then how to route to different controllers while maintaining the same URL route, here's how I was able to accomplish that. Continuing with Users
here, I added a UsersFormController.cs
and updated my Program.cs
to include the route mapping of:
app.MapControllerRoute("usersForm", "/Users/{action}/{id?}", new { controller="UsersForm", action="Index" });
So now I can do /Users/Add/{id}
and /Users/Edit/{id}
and such using that UsersFormController.cs
.