<" /> <" /> <"/>

Iterate over objects of an Enum in C# View

259 views Asked by At

I have the following cshtml code in a View called ViewDepartment.cshtml

@model DepartmentViewModel

<head>
    <meta charset="iso-8859-1">
</head>
<body>
    <div class="d-flex flex-row">
        <a class="navbar-text">@Html.DisplayNameFor(o => o.HeadOfTheDepartment): </a>
        <a class="nav-link" asp-action="Profile" asp-controller="Faculty" asp-route-Id="@Model.HeadOfTheDepartment.FacultyId">@Html.DisplayFor(o => o.HeadOfTheDepartment.FacultyName)</a>
    </div>
    @*Professors*@
    <div class="d-flex flex-column">
        <a class="navbar-text align-content-center">@Html.DisplayNameFor(o => o.Professors): </a>
        <table class="table table-bordered">
            <thead>
                <tr class="d-table-row">
                    <th>@Html.DisplayNameFor(o => o.Professors.First().FacultyName)</th>
                    <th>@Html.DisplayNameFor(o => o.Professors.First().FacultyDepartment)</th>
                    <th>@Html.DisplayNameFor(o => o.Professors.First().FacultyDesignation)</th>
                    <th>@Html.DisplayNameFor(o => o.Professors.First().FacultyAddress)</th>
                    <th>@Html.DisplayNameFor(o => o.Professors.First().FacultyEmail)</th>
                    <th>@Html.DisplayNameFor(o => o.Professors.First().FacultyPhoneNumber)</th>
                    <th>@Html.DisplayNameFor(o => o.Professors.First().FacultyStatus)</th>
                    <th></th>
                </tr>
            </thead>
            <tbody>
                @foreach (Faculty faculty in Model.Professors)
                {
                    <tr class="d-table-row">
                        <td>@Html.DisplayFor(o => faculty.FacultyName)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyDepartment.DepartmentName)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyDesignation)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyAddress)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyEmail)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyPhoneNumber)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyStatus)</td>
                        <td><a class="nav-link" asp-action="Profile" asp-controller="Faculty" asp-route-Id="@faculty.FacultyId">View Profile</a></td>
                    </tr>
                }
            </tbody>
        </table>
    </div>
    @*Associate Professors*@
    <div class="d-flex flex-column">
        <a class="navbar-text align-content-center">@Html.DisplayNameFor(o => o.AssociateProfessors): </a>
        <table class="table table-bordered">
            <thead>
                <tr class="d-table-row">
                    <th>@Html.DisplayNameFor(o => o.AssociateProfessors.First().FacultyName)</th>
                    <th>@Html.DisplayNameFor(o => o.AssociateProfessors.First().FacultyDepartment)</th>
                    <th>@Html.DisplayNameFor(o => o.AssociateProfessors.First().FacultyDesignation)</th>
                    <th>@Html.DisplayNameFor(o => o.AssociateProfessors.First().FacultyAddress)</th>
                    <th>@Html.DisplayNameFor(o => o.AssociateProfessors.First().FacultyEmail)</th>
                    <th>@Html.DisplayNameFor(o => o.AssociateProfessors.First().FacultyPhoneNumber)</th>
                    <th>@Html.DisplayNameFor(o => o.AssociateProfessors.First().FacultyStatus)</th>
                    <th></th>
                </tr>
            </thead>
            <tbody>
                @foreach (var faculty in Model.AssociateProfessors)
                {
                    <tr class="d-table-row">
                        <td>@Html.DisplayFor(o => faculty.FacultyName)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyDepartment.DepartmentName)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyDesignation)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyAddress)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyEmail)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyPhoneNumber)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyStatus)</td>
                        <td><a class="nav-link" asp-action="Profile" asp-controller="Faculty" asp-route-Id="@faculty.FacultyId">View Profile</a></td>
                    </tr>
                }
            </tbody>
        </table>
    </div>
    @*Assistant Professors*@
    <div class="d-flex flex-column">
        <a class="navbar-text align-content-center">@Html.DisplayNameFor(o => o.AssistantProfessors): </a>
        <table class="table table-bordered">
            <thead>
                <tr class="d-table-row">
                    <th>@Html.DisplayNameFor(o => o.AssistantProfessors.First().FacultyName)</th>
                    <th>@Html.DisplayNameFor(o => o.AssistantProfessors.First().FacultyDepartment)</th>
                    <th>@Html.DisplayNameFor(o => o.AssistantProfessors.First().FacultyDesignation)</th>
                    <th>@Html.DisplayNameFor(o => o.AssistantProfessors.First().FacultyAddress)</th>
                    <th>@Html.DisplayNameFor(o => o.AssistantProfessors.First().FacultyEmail)</th>
                    <th>@Html.DisplayNameFor(o => o.AssistantProfessors.First().FacultyPhoneNumber)</th>
                    <th>@Html.DisplayNameFor(o => o.AssistantProfessors.First().FacultyStatus)</th>
                    <th></th>
                </tr>
            </thead>
            <tbody>
                @foreach (var faculty in Model.AssistantProfessors)
                {
                    <tr class="d-table-row">
                        <td>@Html.DisplayFor(o => faculty.FacultyName)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyDepartment.DepartmentName)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyDesignation)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyAddress)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyEmail)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyPhoneNumber)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyStatus)</td>
                        <td><a class="nav-link" asp-action="Profile" asp-controller="Faculty" asp-route-Id="@faculty.FacultyId">View Profile</a></td>
                    </tr>
                }
            </tbody>
        </table>
    </div>
    @*Lecturers*@
    <div class="d-flex flex-column">
        <a class="navbar-text align-content-center">@Html.DisplayNameFor(o => o.Lecturers): </a>
        <table class="table table-bordered">
            <thead>
                <tr class="d-table-row">
                    <th>@Html.DisplayNameFor(o => o.Lecturers.First().FacultyName)</th>
                    <th>@Html.DisplayNameFor(o => o.Lecturers.First().FacultyDepartment)</th>
                    <th>@Html.DisplayNameFor(o => o.Lecturers.First().FacultyDesignation)</th>
                    <th>@Html.DisplayNameFor(o => o.Lecturers.First().FacultyAddress)</th>
                    <th>@Html.DisplayNameFor(o => o.Lecturers.First().FacultyEmail)</th>
                    <th>@Html.DisplayNameFor(o => o.Lecturers.First().FacultyPhoneNumber)</th>
                    <th>@Html.DisplayNameFor(o => o.Lecturers.First().FacultyStatus)</th>
                    <th></th>
                </tr>
            </thead>
            <tbody>
                @foreach (var faculty in Model.Lecturers)
                {
                    <tr class="d-table-row">
                        <td>@Html.DisplayFor(o => faculty.FacultyName)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyDepartment.DepartmentName)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyDesignation)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyAddress)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyEmail)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyPhoneNumber)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyStatus)</td>
                        <td><a class="nav-link" asp-action="Profile" asp-controller="Faculty" asp-route-Id="@faculty.FacultyId">View Profile</a></td>
                    </tr>
                }
            </tbody>
        </table>
    </div>
</body>

It can be seen that for 4 different designations, namely, Professor, AssociateProfessor, AssistantProfessor and Lecturer, all of which are of the type Designation which is an Enum:

public Enum Designation
{
    Professor,
    AssociateProfessor,
    AssistantProfessor,
    Lecturer
}

I had to write the same code framework, namely,

@*Professors*@
<div class="d-flex flex-column">
    <a class="navbar-text align-content-center">@Html.DisplayNameFor(o => o.Professors): </a>
    <table class="table table-bordered">
        <thead>
            <tr class="d-table-row">
                <th>@Html.DisplayNameFor(o => o.Professors.First().FacultyName)</th>
                <th>@Html.DisplayNameFor(o => o.Professors.First().FacultyDepartment)</th>
                <th>@Html.DisplayNameFor(o => o.Professors.First().FacultyDesignation)</th>
                <th>@Html.DisplayNameFor(o => o.Professors.First().FacultyAddress)</th>
                <th>@Html.DisplayNameFor(o => o.Professors.First().FacultyEmail)</th>
                <th>@Html.DisplayNameFor(o => o.Professors.First().FacultyPhoneNumber)</th>
                <th>@Html.DisplayNameFor(o => o.Professors.First().FacultyStatus)</th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            @foreach (Faculty faculty in Model.Professors)
            {
                <tr class="d-table-row">
                    <td>@Html.DisplayFor(o => faculty.FacultyName)</td>
                    <td>@Html.DisplayFor(o => faculty.FacultyDepartment.DepartmentName)</td>
                    <td>@Html.DisplayFor(o => faculty.FacultyDesignation)</td>
                    <td>@Html.DisplayFor(o => faculty.FacultyAddress)</td>
                    <td>@Html.DisplayFor(o => faculty.FacultyEmail)</td>
                    <td>@Html.DisplayFor(o => faculty.FacultyPhoneNumber)</td>
                    <td>@Html.DisplayFor(o => faculty.FacultyStatus)</td>
                    <td><a class="nav-link" asp-action="Profile" asp-controller="Faculty" asp-route-Id="@faculty.FacultyId">View Profile</a></td>
                </tr>
            }
        </tbody>
    </table>
</div>

4 times! (changing the Professors to Lecturers and the likes, of course)


This seems redundant, and I wanted to know if there was a way that would allow me to write the framework only once which will run for all 4 types of Designation. That way, even if the any more Designations get added later, it'll automatically be accomodated in the current code.

Professors (and its sister properties) are of the type IList<Faculty>, where Faculty class is as follows:

public class Faculty
{
    [Required]
    public string FacultyId { get; set; }
    
    [Required]
    public string FacultyName { get; set; }
    
    [Required]
    public FacultyStatus FacultyStatus { get; set; }
    
    [Required]
    public Designation FacultyDesignation { get; set; }

    [Required]
    [DataType(DataType.EmailAddress)]
    public string FacultyEmail { get; set; }

    [Required]
    [DataType(DataType.PhoneNumber)]
    public string FacultyPhoneNumber { get; set; }

    [Required]
    public string FacultyAddress { get; set; }
    
    [Required]
    public int FacultyExperience { get; set; }
    
    [Required]
    public string FacultyQualifications { get; set; }
    
    [Required]
    public string FacultyDescription { get; set; }

    public Department HODDepartment { get; set; }

    [ForeignKey("FacultyDepartment")]
    public string FacultyDepartmentId { get; set; }
    public Department FacultyDepartment{ get; set; }

}


I tried an approach using reflection (available in the previous version of the post) not very succesfully. I am guessing, that some version of ForEach loop will be required, which will loop over an object of Enum.GetNames(typeof(Designation)).Cast<Designation>(). However, I am unable to figure out the Reflection constructs I'll need, for I am pretty new to the concept of Reflection. I need someone to point me in the right direction.

1

There are 1 answers

10
Dave Jellison On BEST ANSWER

I'm working off of this as a base of what your domain resembles based on your question.

public enum Designation
{
    professor,
    associate_professor,
    assistant_professor,
    lecturer
}

public static class ReflectionHelpers
{
    // this code can go anywhere and doesn't need to belong in your domain
    public static string GetAttributeDisplayName(PropertyInfo property)
    {
        var atts = property.GetCustomAttributes(
            typeof(DisplayNameAttribute), true);
        if (atts.Length == 0)
            return null;
        return (atts[0] as DisplayNameAttribute).DisplayName;
    }
}

public class Teacher
{
    public int FacultyId { get; }

    [DisplayName("Name")]
    public string FacultyName { get; set; }

    [DisplayName("Department")]
    public FacultyDepartment FacultyDepartment { get; set; }

    [DisplayName("Designation")]
    public string FacultyDesignation { get; set; }

    [DisplayName("Address")]
    public string FacultyAddress { get; set; }

    [DisplayName("Email")]
    public string FacultyEmail { get; set; }

    [DisplayName("Phone")]
    public string FacultyPhoneNumber { get; set; }

    [DisplayName("Status")]
    public string FacultyStatus { get; set; }
}

public class FacultyDepartment
{
    [DisplayName("Department Name")]
    public string DepartmentName { get; }
}

[AttributeUsage(AttributeTargets.Property)]
public class DesignationAttribute : Attribute
{
    public Designation Designation;

    public DesignationAttribute(Designation designation)
    {
        Designation = designation;
    }
}

public class DomainModel
{
    [DisplayName("Professors")]
    [Designation(Designation.professor)]
    public List<Teacher> Professors { get; } = new List<Teacher>();

    [DisplayName("Associate Professors")]
    [Designation(Designation.associate_professor)]
    public List<Teacher> AssociateProfessors { get; } = new List<Teacher>();

    [DisplayName("Assistant Professors")]
    [Designation(Designation.assistant_professor)]
    public List<Teacher> AssistantProfessors { get; } = new List<Teacher>();

    [DisplayName("Lecturers")]
    [Designation(Designation.lecturer)]
    public List<Teacher> Lecturers { get; } = new List<Teacher>();

    public DomainModel()
    {
        Professors.Add(new Teacher { FacultyName = "Professor 1" });
        AssociateProfessors.Add(new Teacher { FacultyName = "Associate Professor 1" });
        AssistantProfessors.Add(new Teacher { FacultyName = "Assistant Professor 1" });
        Lecturers.Add(new Teacher { FacultyName = "Lecturer 1" });
    }
}

What we need here is reflection. Now, we'll iterate the Enum.GetValues() rather than the GetNames() and find the appropriate Model property based on the custom attribute we assigned. If your Model's properties have the same name as the enum value, you can throw out the DesignationAttribute bits. In the view, we do your same loop basically, but immediately lookup the property we're working with via reflection. (research more on this topic to get a better understanding, no need to regurgitate basic reflection here).

I get your main Model DisplayName from the reflected ProperyInfo that we already have in our enum loop. I do this via a ReflectionHelpers static method but you can move this method anywhere, such as a top-level library.

We then use modelProperty.GetValue(Model) to reflect the value of the property back from the Model instance so we can iterate the list contents of that property.

The rest is just as you had it previously.

@using System.Reflection;

@model DomainModel
@{
    ViewData["Title"] = "Home Page";

    var modelProperties = Model.GetType().GetProperties();
    var teacherProperties = typeof(Teacher).GetProperties();
}

@foreach (var designation in Enum.GetValues<Designation>()) // use GetValues now
{
    var modelProperty =
        modelProperties
            .FirstOrDefault(p => p.GetCustomAttribute<DesignationAttribute>().Designation == designation);

    <div class="d-flex flex-column">
        <a class="navbar-text align-content-center">@ReflectionHelpers.GetAttributeDisplayName(modelProperty): </a>
        <table class="table table-bordered">
            <thead>
                <tr class="d-table-row">
                    @foreach (var teacherProperty in teacherProperties)
                    {
                        <th>@ReflectionHelpers.GetAttributeDisplayName(teacherProperty)</th>
                    }
                    <th></th>
                </tr>
            </thead>
            <tbody>
                @foreach (var faculty in modelProperty.GetValue(Model) as IList<Teacher>)
                {
                    <tr class="d-table-row">
                        <td>@Html.DisplayFor(o => faculty.FacultyName)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyDepartment.DepartmentName)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyDesignation)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyAddress)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyEmail)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyPhoneNumber)</td>
                        <td>@Html.DisplayFor(o => faculty.FacultyStatus)</td>
                        <td>
                            <a class="nav-link" asp-action="Profile" asp-controller="Faculty" asp-route-Id="@faculty.FacultyId">View Profile</a>
                        </td>
                    </tr>
                }
            </tbody>
        </table>
    </div>
}