Adam Stepniak Adam Stepniak - 1 month ago 5
C# Question

Access to types declared outside @model scope in MVC

I have created a PersonList class which contains lists of different groups of peoples. In my controller I will be passing that class to the view. My view is strongly typed so I reference to that class. But my view requires types of that groups (Student, Retired, Politic) so I can use them in conditional statement in View. I solved that problem by adding one object per group in PersonList class but I feel that is not best way to do it. Please direct me to better solution.

.cs file:


public interface IPerson
{
}
public abstract class Person
{
public string Name { get; set; }
public string LastName { get; set; }
public Int16 Age { get; set; }
}
public class Politics : Person, IPerson
{
public string Party { get; set; }
public int Experience { get; set; }
}
public class Students : Person, IPerson
{
public string Major { get; set; }
public int Year { get; set; }
}
public class Retired: Person, IPerson
{
public int Pension { get; set; }
public string Hobby { get; set; }
}
public class PersonLists : IPerson
{
public List<Retired> AllRetired = new List<Retired>();
public List<IPerson> AllPeople = new List<IPerson>();
public Students AStudent = new Students();
public Politics APolitic = new Politics();
public Retired ARetired = new Retired();
}



cshtml file:

> @model ConferenceAttendees.Models.PersonLists
<style>
h6 {
display: inline
}
th{
text-align:center;

}
</style>
<div>
<table style="width:100%" align="left">
<tr>
<th>Imie</th>
<th>Nazwisko</th>
<th>Wiek</th>
<th>Partia</th>
<th>Doswiadczenie</th>
<th>Kierunek</th>
<th>Rok</th>
</tr>
@foreach (dynamic item in Model.AllPeople)
{
<tr>
<th>@item.Name</th>
<th>@item.LastName</th>
<th>@item.Age</th>
@if (item.GetType() == Model.APolitic.GetType())
{
<th>@item.Party</th>
<th>@item.Experience</th>
<th>b.d</th>
<th>b.d</th>
}
@if (item.GetType() == Model.AStudent.GetType())
{
<th>b.d</th>
<th>b.d</th>
<th>@item.Major</th>
<th>@item.Year</th>
}
</tr>
}
</table>
</div>

Answer

First, you can import a namespace in your Razor view via the @using directive. You can do this before the @model directive. Then, you can use your types Students and Politics in your view.

Second, to check if item is of a concrete type, you can use the as operator and see if the result is not null. So, you don't need to call GetType() on your instances. Now you can remove your AStudent, APolitic and ARetired instances.

Also, two side notes to your code:

  1. Do not use dynamic in the foreach loop. You don't need dynamic typing here. Instead, use Person as type for the item variable. For this to work, AllPeople must be of type List<Person> instead of List<IPerson>. In fact, you do not need the IPerson interface at all since you don't define anything in there.
  2. I suggest not using the plural form for your classes (Students, Politics). One instance of Students is just one student, so why not call the class Student? Same for Politics.

All in all, your view now looks like this:

@using ConferenceAttendees.Models
@model ConferenceAttendees.Models.PersonLists
<style>
h6 {
    display: inline
}
th{
    text-align:center;

}
</style>
<div>
    <table style="width:100%" align="left">
        <tr>
            <th>Imie</th>
            <th>Nazwisko</th>
            <th>Wiek</th>
            <th>Partia</th>
            <th>Doswiadczenie</th>
            <th>Kierunek</th>
            <th>Rok</th>
        </tr>
            @foreach (Person item in Model.AllPeople)
            {
              var student = item as Student;
              var politician = item as Politician;
              <tr>
              <th>@item.Name</th>
              <th>@item.LastName</th>
              <th>@item.Age</th> 
                @if (politician != null)
                {
                    <th>@item.Party</th>
                    <th>@item.Experience</th>
                    <th>b.d</th>
                    <th>b.d</th>
                }
                @if (student != null)
                {
                    <th>b.d</th>
                    <th>b.d</th>
                    <th>@item.Major</th>
                    <th>@item.Year</th>
                }
              </tr>
            }
    </table>
</div>