In ASP.NET and Entity Framework, I have a many-to-many relationship for user and classroom. I'm trying to get a list of students in a class. So far I know 2 solutions: create a user/class view model, or pass around a ViewBag.ClassId
between controller and view.
Is there a way for a view page that use
@model IEnumerable<Models.User>
to get info from Model.Class
without creating a new view model?
I'm used to java jsp where I can pass any kind of variable, so I'm new to this view/model restriction.
(The user and class model are scaffolded from the database with table "userclass" as a join table).
So far I've just been passing a viewbag.classId
to every controller and make that the parameter:
public async Task<IActionResult> ClassStudents(int id)
{
var usersInClass = await _context.Classes
.Where(cls => cls.Id == id)
.Include(cls => cls.Users) // Eager loading to load associated users
.Select(cls => cls.Users.Where(user => user.RoleSettingId == 5))
.FirstOrDefaultAsync();
ViewBag.ClassId = id;
return View("/Views/Classroom/ClassStudents.cshtml", usersInClass);
}
Since the User
and Classroom
class has already configured many to many relationships, you can use Navigation properties to display the class information from the Model.User.
Refer to the following sample:
My models:
public class User
{
public int ID { get; set; }
public string Name { get; set; }
public IList<Classroom> Classrooms { get; set; } = new List<Classroom>();
}
public class Classroom
{
[Key]
public int CID { get; set; }
public string CName { get; set; }
public IList<User> User { get; set; } = new List<User>();
}
My dbcontext:
public SchoolDbcontext(DbContextOptions<SchoolDbcontext> options)
: base(options) { }
public DbSet<User> User { get; set; }
public DbSet<Classroom> Classrooms { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Classroom>().HasMany(x => x.User).WithMany(st => st.Classrooms);
}
Controller:
public async Task<IActionResult> ClassStudentsAsync(int id)
{
var classnum = _context.Classrooms.Include(x=>x.User).FirstOrDefault(cls => cls.CID == id);
var stu = new List<User>();
for (var i = 0; i < classnum.User.Count; i++)
{
var students = classnum.User[i];
stu.Add(students);
}
return View(stu);
}
My CSHTML:
@using WebApplication1.Models
@model IEnumerable<WebApplication1.Models.User>
<h1>Student List</h1>
<table>
<thead>
<tr>
<th>Student ID</th>
<th>Name</th>
<th>ClassName</th>
</tr>
</thead>
<tbody>
@foreach (var user in Model)
{
<tr>
<td>@user.ID</td>
<td>@user.Name</td>
<td>@user.Classrooms.FirstOrDefault().CName</td>
</tr>
}
</tbody>
</table>
When I give class id: 2
Besides, about the query statement, you can check the following statement: The result is a list of User.
In my sample code, the User
and ClassRooms
are configured many to many relationship and the UserClass
is the join table.
var userlist = _dbContext.ClassRooms.Where(c => c.ClassID == id)
.Include(cls => cls.UserClass.Where(c => c.ClassId == id)) // Eager loading to load associated users
.ThenInclude(uc => uc.User)
.SelectMany(cls => cls.UserClass.Select(c=>c.User)).ToList();
var userlist2 = _dbContext.User.Include(u => u.UserClass.Where(c=>c.ClassId == id))
.ThenInclude(c=>c.ClassRoomes).Where(cls => cls.UserClass.Any(c => c.ClassId == id)).ToList();