Build Master-Detail Pages in ASP.NET Core MVC — Part 1
Master-detail pages are a staple in many web applications, offering a structured way to display and manage related data. Developers can implement these pages using various approaches—server-side, client-side, or hybrid techniques. Additionally, a wide range of third-party controls and plugins are available to simplify the process. However, for beginners, it's valuable to understand the underlying mechanics of master-detail interactions and how to build them from the ground up. This multipart article explores how to create master-detail pages in ASP.NET Core using a purely server-side approach, without relying on any third-party components or libraries.
I originally wrote this article series a few years ago, based on the .NET framework and tooling available at the time. Since then, the ecosystem has evolved dramatically—bringing in new capabilities, improved performance, and modern development workflows. With the release of .NET 9 and the growing adoption of Visual Studio Code, I decided it was time for a complete rewrite. This updated series features a fresh codebase and revised content that reflects current framework version and project templates.
Before diving into the technical details, let's take a look at the sample application we'll be building throughout this article series. This will give you a clear picture of the end goal and help contextualize each step of the development process.
The sample application will use two SQL Server tables: Teams and TeamMembers. The Teams table serves as the master and holds information about each team, including TeamID, Name, and Description. Each team can have one or more associated members, which are stored in the TeamMembers table. This table acts as the detail component and includes fields such as TeamMemberID, Name, and Email.
Take a look at the image below, which shows the main page of the application.
As shown in the image, the Teams records are displayed in a tabular format. You can add a new team by clicking the Insert Team button. To update existing team details, use the Manage Teams button. To manage the members associated with a team, click the Manage Members button.
The image below displays the master user interface that appears when you click the Manage Team button.
When you select a team for editing, the corresponding record is highlighted in the master grid, and its details appear below the grid for modification. You can click Edit to update the team information, Delete to remove the team, or Cancel to exit the editing mode and clear the selection.
Clicking the Insert Team button opens a similar interface, allowing you to add a new team by entering its details.
When you click the Manage Members button for a specific team in the master grid, a list of associated team members is displayed in the detail grid below. This allows you to view and manage the members linked to the selected team.
The detail grid also supports member editing. When you click the Manage Member button for a selected entry, the member's details are loaded into an editable form, allowing you to update their information as needed.
The Insert Member button functions similarly to the Insert Team button, but instead of creating a new team, it adds a new member to the currently selected team.
Now that you have a general overview of the application, let's examine the structure of the Teams and TeamMembers tables. For this example, I've added these tables to the existing Northwind database using SQL Server Management Studio. However, you're free to create a new database if that better suits your setup.
The Teams table is shown below:
The Teams table contains three columns: TeamID, Name, and Description. The TeamID column is configured as an identity column and serves as the primary key for the table.
The TeamMembers table is shown next:
The TeamMembers table consists of four columns: TeamMemberID, TeamID, Name, and Email. The TeamMemberID column is an identity column and serves as the primary key for the table. The TeamID column is a foreign key that references the TeamID column in the Teams table, establishing a relationship between team members and their respective teams.
To add a foreign key constraint in SQL Server Management Studio (SSMS), expand the TeamMembers table in Object Explorer. Then, right-click the Keys folder and select New Foreign Key from the shortcut menu.
This opens a dialog where you can configure the foreign key relationship, specifying TeamID in TeamMembers as the referencing column and TeamID in Teams as the referenced column.
Once you've added the foreign key constraint as described above, close any open table designers and exit SQL Server Management Studio (SSMS) to complete the setup.
Now that the database tables are set up, we can proceed to create an Entity Framework Core model for data access. Start by creating a new ASP.NET Core MVC project. Once the project is ready, add the NuGet package Microsoft.EntityFrameworkCore.SqlServer to enable SQL Server support within Entity Framework Core.
Next, add a folder named DataAccess under the project root. Inside this folder, create three class files: Team.cs, TeamMember.cs, and AppDbContext.cs. These files will define your entity models and the database context used by Entity Framework Core.
Open TeamMember.cs file and add entity class as shown below:
public class TeamMember
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int TeamMemberID { get; set; }
public int TeamID { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
The TeamMember entity class is straightforward, containing all the essential properties that align with the schema of the TeamMembers table.
Next, open Team.cs file and add Team entity class to it as shown below:
public class Team
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int TeamID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
[ForeignKey("TeamID")]
public List<TeamMember> Members { get; set; }
}
Note the Members navigation property, which holds the collection of TeamMember entities associated with a given Team. This property will be loaded explicitly via code, as detailed in the next part of this article series.
Finally, open the AppDbContext.cs file and add the AppDbContext class as shown below:
public class AppDbContext:DbContext
{
public DbSet<Team> Teams { get; set; }
public DbSet<TeamMember> TeamMembers { get; set; }
public AppDbContext(DbContextOptions
<AppDbContext> options):base(options)
{
}
}
The AppDbContext class contains two DbSet properties: Teams and TeamMembers, which represent the corresponding tables in the database.
You can register the AppDbContext with the dependency injection (DI) container in the application's Program.cs file. To do this, open the Program.cs file and add the following code:
builder.Services.AddDbContext<AppDbContext>
(o => o.UseSqlServer
(builder.Configuration.
GetConnectionString("AppDb")));
In this setup, the database connection string is retrieved from the appsettings.json file and passed to the UseSqlServer() method. This enables Entity Framework Core to establish a connection with the specified SQL Server database.
The connection string stored in the appsettings.json looks like this:
"ConnectionStrings": {
"AppDb": "data source=.;initial
catalog=Northwind;integrated security=true"
}
Make sure to update the database connection string to match your specific development environment.
In the next part of this article series, we'll explore the overall structure of the web application and begin implementing the controllers.
That's all for now—may your syntax stay sharp, and your intentions stay kind. With this quiet wish, I set my pen down with care.