Perform Master Detail CRUD operations in ASP.NET Core (Part 2)

In the previous part of  this article series you were introduced to the sample application we are building. You also created the EF Core model consisting of Team, TeamMember, and AppDbContext classes. In this part we will add TeamsController to the web application perform CRUD operations on the Teams table.

Open the same project that we created in the previous part and add two enumerations to the Models folder named DataDisplayModes and DataEntryTargets. These enums are shown below:

public enum DataDisplayModes
{
    Read,
    Insert,
    Update
}
public enum DataEntryTargets
{
    Teams,
    TeamMembers
}

The DataDisplayModes enum is intended to indicate the display mode of the data entry UI such as read-only, Insert, and Update. Based on this value we will render the appropriate UI. The DataEntryTargets enum indicates whether we are editing Teams table or TeamMembers table.

Now add a view model class to the Models folder named MasterDetailViewModel and add the following properties to it:

public class MasterDetailViewModel
{
    public List<Team> Teams { get; set; }
    public Team SelectedTeam { get; set; }
    public TeamMember SelectedTeamMember { get; set; }
    public DataEntryTargets DataEntryTarget { get; set; }
    public DataDisplayModes DataDisplayMode { get; set; }

}

As you can see the MasterDetailViewModel class contains five properties namely Teans, SelectedTeam, SelectedTeamMember, DataEntryTarget, and DataDisplayMode.

The Teams property points to the list of Teams entities that are to be displayed in the master grid. The SelectedTeam property is a reference to the Team that is being selected by clicking the Manage Team button. The SelectedTeamMember is a TeamMember selected from the detail grid for editing. The DataEntryTarget tells us whether master table is being modified or details table is being edited. The DataDisplayMode indicates the mode for the data entry operation - Read, Insert, or Update.

At this stage your Models folder should resemble this:

Next, add a new controller named TeamsController in the Controllers folder.

The TeamsController is responsible for performing CRUD on Teams table. You need to inject the AppDbContext instance into its constructor as shown below:

public class TeamsController : Controller
{
    private readonly AppDbContext db;

    public TeamsController(AppDbContext db)
    {
        this.db = db;
    }
}

Then we will write List() action for displaying a list of Teams. The List() action is shown below:

public IActionResult List()
{
    MasterDetailViewModel model = new 
MasterDetailViewModel
    {
        Teams = db.Teams.ToList(),
        SelectedTeam = null,
        SelectedTeamMember = null,
        DataEntryTarget = DataEntryTargets.Teams,
        DataDisplayMode = DataDisplayModes.Read
    };
    return View("Main", model);
}

We create a MasterDetailViewModel object and fill its properties. Especially the Teams property is important because it is used on the view to render the Teams master grid.

Notice that the List() action returns Main view. We will create the Main view in the later parts of this article series. The Main view renders the list of teams like this:

When you click on the Manage Team button of the master grid, that row is highlighted and that Team is shown for further editing. This is accomplished by Select() action as shown below:

[HttpPost]
public IActionResult Select(int teamId)
{
    MasterDetailViewModel model = new 
MasterDetailViewModel
    {
        Teams = db.Teams.ToList(),
        SelectedTeam = db.Teams.Find(teamId),
        SelectedTeamMember = null,
        DataEntryTarget = DataEntryTargets.Teams,
        DataDisplayMode = DataDisplayModes.Read
    };

    return View("Main", model);
}

The Select() action receives teamId route parameter. Inside, we form a MasterDetailViewModel object. This time we also set the SelectedTeam property to a team whose teamId is passed.

Upon selecting a Team it is shown in the master grid like this:

Next, we need two actions that take care of adding a new Team - InsertEntry() and InsertSave(). These actions are shown below:

[HttpPost]
public IActionResult InsertEntry()
{
    MasterDetailViewModel model = new 
MasterDetailViewModel
    {
        Teams = db.Teams.ToList(),
        SelectedTeam = null,
        SelectedTeamMember = null,
        DataEntryTarget = DataEntryTargets.Teams,
        DataDisplayMode = DataDisplayModes.Insert
    };
    return View("Main", model);
}

[HttpPost]
public IActionResult InsertSave(Team team)
{
    db.Teams.Add(team);
    db.SaveChanges();

    MasterDetailViewModel model = new 
MasterDetailViewModel
    {
        Teams = db.Teams.ToList(),
        SelectedTeam = db.Teams.Find(team.TeamID),
        SelectedTeamMember = null,
        DataEntryTarget = DataEntryTargets.Teams,
        DataDisplayMode = DataDisplayModes.Read
    };

    return View("Main", model);
}

The InsertEntry() action gets invoked when you click on the Insert Team button at the top of the master grid. Inside, we set the DataEntryTarget to Teams because we are adding a new Team. We also set the DataDisplayMode to Insert because we want to render UI like this:

When you enter Team details such as Name and Description, and hit Save, the InsertUpdate() action gets called. Inside, we use Add() and SaveChanges() method to add a new Team. We then set the newly added Team as the selected item in the master grid. This is done by setting the SelectedTeam property of MasterDetailViewModel object. The DataDisplayMode is changed to Read so that the newly added Team is shown like this:

We will now write two actions to take care of update operation. These actions namely UpdateEntry() and UpdateSave() are shown below.

[HttpPost]
public IActionResult UpdateEntry(int teamId)
{
    MasterDetailViewModel model = new 
MasterDetailViewModel
    {
        Teams = db.Teams.ToList(),
        SelectedTeam = db.Teams.Find(teamId),
        SelectedTeamMember = null,
        DataEntryTarget = DataEntryTargets.Teams,
        DataDisplayMode = DataDisplayModes.Update
    };

    return View("Main", model);
}


[HttpPost]
public IActionResult UpdateSave(Team team)
{
    db.Teams.Update(team);
    db.SaveChanges();

    MasterDetailViewModel model = new 
MasterDetailViewModel
    {
        Teams = db.Teams.ToList(),
        SelectedTeam = team,
        SelectedTeamMember = null,
        DataEntryTarget = DataEntryTargets.Teams,
        DataDisplayMode = DataDisplayModes.Read
    };
    return View("Main", model);
}

The UpdateEntry() action is invoked when you click the Edit button. We pass teamId route parameter to this action. Inside, we fetch that particular Team and set the SelectedTeam property of MasterDetailViewModel. We also set the DataDisplayMode to Update since we want to render UI for update:

When you edit a Team record and click on the Save button, UpdateSave() action is called. Inside, we use Update() and SaveChanges() methods to update that Team record. We then toggle the DataDisplayMode to Read because edit operation is now over.

If you click on the Delete button (see earlier figures) when a Team is being displayed for editing, Delete() action is invoked.

[HttpPost]
public IActionResult Delete(int teamId)
{
    Team team = db.Teams.Find(teamId);
    db.Teams.Remove(team);
    db.SaveChanges();

    MasterDetailViewModel model = new 
MasterDetailViewModel
    {
        Teams = db.Teams.ToList(),
        SelectedTeam = null,
        SelectedTeamMember = null,
        DataEntryTarget = DataEntryTargets.Teams,
        DataDisplayMode = DataDisplayModes.Read
    };
    return View("Main", model);
}

Inside the Delete() action, we find the Team to be deleted and then use Remove() and SaveChanges() methods to delete that record. Since the record is now deleted, we also set the SelectedTeam property of MasterDetailViewModel to null.

If you decide to cancel the data entry mode and return back to read mode for the selected team you can hit the Cancel button. Doing so triggers the CancelEntry() action shown below:

[HttpPost]
public IActionResult CancelEntry(int teamId)
{
    MasterDetailViewModel model = new 
MasterDetailViewModel
    {
        Teams = db.Teams.ToList(),
        SelectedTeam = db.Teams.Find(teamId),
        SelectedTeamMember = null,
        DataEntryTarget = DataEntryTargets.Teams,
        DataDisplayMode = DataDisplayModes.Read
    };
    return View("Main", model);
}

We do this by simply setting the DataDisplayMode to Read. Note that the Team is still selected in the master grid but it is no longer in data entry mode.

Finally, we need an action that cancels the Team selection. For example, when we click on Insert Team button we need to remove the current selection because a new record is being added. This is accomplished by CancelSelection() action as shown below:

[HttpPost]
public IActionResult CancelSelection()
{
    MasterDetailViewModel model = new 
MasterDetailViewModel
    {
        Teams = db.Teams.ToList(),
        SelectedTeam = null,
        SelectedTeamMember = null,
        DataEntryTarget = DataEntryTargets.Teams,
        DataDisplayMode = DataDisplayModes.Read
    };
    return View("Main", model);
}

Inside the CancelSelection() action we set the SelectedTeam property of MasterDetailViewModel to null.

This completes the TeamsController. Note that for the sake of brevity we haven't paid much attention to model validation and error handling. You can add those capabilities using data validation attributes as you normally do in any ASP.NET Core application.

In the next part of this series, we will add TeamMembersController that performs CRUD on detail records. 

That's it for now! Keep coding!!


Bipin Joshi is an independent software consultant, trainer, author, and meditation teacher. He has been programming, meditating, and teaching for 25+ years. He conducts instructor-led online training courses in ASP.NET family of technologies for individuals and small groups. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced the Yoga way of life he also teaches Ajapa Yoga to interested individuals. To know more about him click here.

Get connected : Facebook  Twitter  LinkedIn  YouTube

Posted On : 08 March 2021


Tags : ASP.NET ASP.NET Core SQL Server MVC C# Visual Studio