'How do I show a list of group members that belong to a group in blazor?

Below is my groupmember Class

namespace MMSProjectShared
{
    public  class GroupMember
    {
        [Key]
        public int MemberId { get; set; }
        public int MemberNumber { get; set; }
        public string Nrc { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Address { get; set; }
        public string PhoneNumber { get; set; }
        public int GroupId { get; set; }
        public Group Group { get; set; }
    }
}

And this is my Group class

namespace MMSProjectShared
{
    public class Group
    {
        [Key]
        public int GroupId { get; set; }
        public string GroupName { get; set; }
        public string Product_Service { get; set; }
        public List<GroupMember> GroupMembers { get; set; }
        public int CategoryId { get; set; }
        public Category Category { get; set; }   
        public ICollection<Loan> Loan { get; set; }


    }
}

I want to be able to show the groupmembers in the group page like below: I am using blazorServer on DotNet 6. enter image description here

Here is what I tried:

<MudTd DataLabel="GroupMembers">
            @foreach(var groupMember in groupMembers)
            {
                <MudSelectItem T="int" Value = "@groupMember.MemberId">@groupMember.LastName @groupMember.FirstName</MudSelectItem>
            }
        </MudTd>

But the problem is that all group members in the database were showing in all groups. Below is the whole Group Page:

@page "/EditGroups"
@using MMSProjectShared
@using System.Linq
@using System
@using static MudBlazor.CategoryTypes
@inject MMSProjectServer.Interfaces.ILoan LoanService
@inject MMSProjectServer.Interfaces.IGroup GroupService
@inject MMSProjectServer.Interfaces.IGroupMember GroupMemberService
@inject MMSProjectServer.Interfaces.ICategory CategoryService
@inject MudBlazor.ISnackbar snackBar


<MudCard Elevation="25">
    <MudCardHeader>
        <CardHeaderContent>
            <MudText Typo="Typo.h6">Add / Edit Groups</MudText>
        </CardHeaderContent>
    </MudCardHeader>
    <MudCardContent>
        <MudTextField @bind-Value="group.GroupName" Label="Name of Group" Variant="Variant.Text" Margin="Margin.Normal"></MudTextField>
        <MudTextField @bind-Value="group.Product_Service" Label="Product or Service" Variant="Variant.Text" Margin="Margin.Normal"></MudTextField>
        <MudSelect T="int" Label="Select Category" @bind-Value = "group.CategoryId">
            @foreach(var category in categories){
        <MudSelectItem T="int" Value = "@category.CategoryId">@category.CategoryName</MudSelectItem>
            }
        </MudSelect>
        
        <br />
        <MudButton Variant="Variant.Filled" Color="Color.Success" OnClick="Save">Save Group</MudButton>
    </MudCardContent>
</MudCard>
<br />
<MudTable Elevation="25" Items="GetGroups()" Filter="new Func<Group, bool>(Search)" @bind-customer="group">
    <ToolBarContent>
        <MudText Typo="Typo.h6">Groups</MudText>
        <MudSpacer />
        <MudTextField @bind-Value="searchString" Placeholder="Search for Groups..." Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField>
    </ToolBarContent>
    <HeaderContent>
        <MudTh>ID</MudTh>
        <MudTh>GroupName</MudTh>
        <MudTh>Product/Service</MudTh>
        <MudTh>GroupMembers</MudTh>
        <MudTh>Loan</MudTh>
        <MudTh>Category</MudTh>
        <MudTh>Action</MudTh>
    </HeaderContent>
    <RowTemplate>
        <MudTd DataLabel="Id">@context.GroupId</MudTd>
        <MudTd DataLabel="Group Name">@context.GroupName</MudTd>
        <MudTd DataLabel="Product/Service">@context.Product_Service</MudTd>
        <MudTd DataLabel="GroupMembers">
            @foreach(var groupMember in groupMembers)
            {
                <MudSelectItem T="int" Value = "@groupMember.MemberId">@groupMember.LastName @groupMember.FirstName</MudSelectItem>
            }
        </MudTd>
        <MudTd DataLabel="Loans">
            
        </MudTd>
        <MudTd DataLabel="Categoty">@context.Category.CategoryName</MudTd>
        <MudTd DataLabel="">
            <MudFab @onclick="@(()=>Edit(@context.GroupId))" Color="Color.Primary" Icon="@Icons.Material.Filled.Edit" Size="Size.Small" IconSize="Size.Small" />
            <MudFab @onclick="@(()=>Delete(@context.GroupId))" Color="Color.Secondary" Icon="@Icons.Material.Filled.Delete" Size="Size.Small" IconSize="Size.Small" />
        </MudTd>
    </RowTemplate>
</MudTable>

@code{
    private string searchString = "";
    private Group group = new Group();
    private List<Group> groups = new List<Group>();
    //List of Categories for drop down list
    private Category category = new Category();
    private List<Category> categories = new List<Category>();
    //Because we want to view the loan number for each group's loans
    private Loan loan = new Loan();
    private List<Loan> loans = new List<Loan>();

    private GroupMember groupMember = new GroupMember();
    private List<GroupMember> groupMembers = new List<GroupMember>();

    protected override async Task OnInitializedAsync()
    {
        GetGroups();
        groupMembers = GroupMemberService.GetGroupMembers();
        loans = LoanService.GetLoans();
        categories = CategoryService.GetCategories();
    }

    private List<Group> GetGroups()
    {
        groups = GroupService.GetGroups();
        return groups;
    }

    //private List<GroupMember> GetGroupMembers()
    //{
    //    groupMembers = GroupMemberService.GetGroupMembers();
    //    return groupMembers;
    //}
    private bool Search(Group group)
    {
        if (string.IsNullOrWhiteSpace(searchString)) return true;
        if (group.GroupName.Contains(searchString, StringComparison.OrdinalIgnoreCase)
            || group.Product_Service.Contains(searchString, StringComparison.OrdinalIgnoreCase)
            || group.Category.CategoryName.Contains(searchString, StringComparison.OrdinalIgnoreCase))
        {
            return true;
        }
        return false;
    }
    private void Save()
    {
        GroupService.SaveGroup(group);
        group = new Group();
        snackBar.Add("Group Saved.", Severity.Success);
        GetGroups();
    }
    private void Edit(int id)
    {
        group = groups.FirstOrDefault(c => c.GroupId == id);
    }
    private void Delete(int id)
    {
        GroupService.DeleteGroup(id);
        snackBar.Add("Group Deleted.", Severity.Success);
        GetGroups();
    }
}


Solution 1:[1]

What you can do to easily achieve this is using navigation properties.
In your GroupService class

public class GroupService: IGroup
{
   ...
   public List<Group> GetGroups()
   {
       return context.Groups.Include(g=>g.GroupMembers)
                            .Include(g=>g.Loans)
                            .Include(g=>g.Category).ToList();
   }
   ...
}

In your razor file

@page "/EditGroups"
@using MMSProjectShared
@using System.Linq
@using System
@using static MudBlazor.CategoryTypes
@inject MMSProjectServer.Interfaces.ILoan LoanService
@inject MMSProjectServer.Interfaces.IGroup GroupService
@inject MMSProjectServer.Interfaces.IGroupMember GroupMemberService
@inject MMSProjectServer.Interfaces.ICategory CategoryService
@inject MudBlazor.ISnackbar snackBar


<MudCard Elevation="25">
    <MudCardHeader>
        <CardHeaderContent>
            <MudText Typo="Typo.h6">Add / Edit Groups</MudText>
        </CardHeaderContent>
    </MudCardHeader>
    <MudCardContent>
        <MudTextField @bind-Value="group.GroupName" Label="Name of Group" Variant="Variant.Text" Margin="Margin.Normal"></MudTextField>
        <MudTextField @bind-Value="group.Product_Service" Label="Product or Service" Variant="Variant.Text" Margin="Margin.Normal"></MudTextField>
        <MudSelect T="int" Label="Select Category" @bind-Value = "group.CategoryId">
            @foreach(var category in categories){
        <MudSelectItem T="int" Value = "@category.CategoryId">@category.CategoryName</MudSelectItem>
            }
        </MudSelect>
        
        <br />
        <MudButton Variant="Variant.Filled" Color="Color.Success" OnClick="Save">Save Group</MudButton>
    </MudCardContent>
</MudCard>
<br />
<MudTable Elevation="25" Items="@groups" Filter="new Func<Group, bool>(Search)" @bind-customer="group">
    <ToolBarContent>
        <MudText Typo="Typo.h6">Groups</MudText>
        <MudSpacer />
        <MudTextField @bind-Value="searchString" Placeholder="Search for Groups..." Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField>
    </ToolBarContent>
    <HeaderContent>
        <MudTh>ID</MudTh>
        <MudTh>GroupName</MudTh>
        <MudTh>Product/Service</MudTh>
        <MudTh>GroupMembers</MudTh>
        <MudTh>Loan</MudTh>
        <MudTh>Category</MudTh>
        <MudTh>Action</MudTh>
    </HeaderContent>
    <RowTemplate>
        <MudTd DataLabel="Id">@context.GroupId</MudTd>
        <MudTd DataLabel="Group Name">@context.GroupName</MudTd>
        <MudTd DataLabel="Product/Service">@context.Product_Service</MudTd>
        <MudTd DataLabel="GroupMembers">
            @if(context.GroupMembers.Any()){
            foreach(var groupMember in context.GroupMembers)
            {
                <MudSelectItem T="int" Value = "@groupMember.MemberId">@groupMember.LastName @groupMember.FirstName</MudSelectItem>
            }
}
else
{
<MudAlert Severity="Severity.Info">No Group Members In this Group</MudAlert>
}
        </MudTd>
        <MudTd DataLabel="Loans">
            
        </MudTd>
        <MudTd DataLabel="Categoty">@context.Category.CategoryName</MudTd>
        <MudTd DataLabel="">
            <MudFab @onclick="@(()=>Edit(@context.GroupId))" Color="Color.Primary" Icon="@Icons.Material.Filled.Edit" Size="Size.Small" IconSize="Size.Small" />
            <MudFab @onclick="@(()=>Delete(@context.GroupId))" Color="Color.Secondary" Icon="@Icons.Material.Filled.Delete" Size="Size.Small" IconSize="Size.Small" />
        </MudTd>
    </RowTemplate>
</MudTable>

@code{
    private string searchString = "";
    private Group group = new Group();
    private List<Group> groups = new List<Group>();
    //List of Categories for drop down list
    private Category category = new Category();
    Loan loan = new Loan();
    private GroupMember groupMember = new GroupMember();
    protected override async Task OnInitializedAsync()
    {
       GetGroups();       
    }
   void GetGroups()
{
 groups = groupService.GetGroups();
StateHasChanged();
}
 
    private bool Search(Group group)
    {
        if (string.IsNullOrWhiteSpace(searchString)) return true;
        if (group.GroupName.Contains(searchString, StringComparison.OrdinalIgnoreCase)
            || group.Product_Service.Contains(searchString, StringComparison.OrdinalIgnoreCase)
            || group.Category.CategoryName.Contains(searchString, StringComparison.OrdinalIgnoreCase))
        {
            return true;
        }
        return false;
    }
    private void Save()
    {
        GroupService.SaveGroup(group);
        group = new Group();
        snackBar.Add("Group Saved.", Severity.Success);
        GetGroups();
    }
    private void Edit(int id)
    {
        group = groups.FirstOrDefault(c => c.GroupId == id);
    }
    private void Delete(int id)
    {
        GroupService.DeleteGroup(id);
        snackBar.Add("Group Deleted.", Severity.Success);
        GetGroups();
    }
}

Solution 2:[2]

You could make groupMembers a dictionary rather than a list, and store the group members there, grouped by group ID.

Example:

//using System.Collections.Generic;

private Dictionary<int, GroupMember> groupMembersByGroupId;

Assuming that GroupMemberService.GetGroupMembers() returns an IEnumerable<GroupMember>, population of your dictionary could be done as follows:

//using System.Linq;

groupMembersByGroupId = GroupMemberService.GetGroupMembers()
    .GroupBy(member => member.GroupId)
    .ToDictionary(gr => gr.Key, gr => gr.Select(member => member));

In your table, you can access the group members for the desired group as follows:

@foreach(var groupMember in groupMembersByGroupId[context.GroupId])
{
    ...
}

Note: groupMembersByGroupId[context.GroupId] will throw an exception if the key (context.GroupId) does not exist in the dictionary; i.e. if the group does not contain any group members. You may want to wrap the foreach loop inside an if to make sure there actually are any group members to be displayed:

if (groupMembersByGroupId.ContainsKey(context.GroupId))
{
    @foreach(var groupMember in groupMembersByGroupId[context.GroupId])
    {
        ...
    }
}

Example fiddle here.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Astrid E.