The following assumes EF Core 5, which now automatically creates many-to-many relationships without you having to add an entity class for the join table (as was necessary in previous versions of EF Core).
// Entity classes
public class Student
{
public int StudentId { get; set; }
public string StudentName { get; set; }
public ICollection<Hobby> Hobbies { get; set; }
public ICollection<Class> Classes { get; set; }
}
public class Class
{
public int ClassId { get; set; }
public string ClassName { get; set; }
public ICollection<Student> Students { get; set; }
}
public class Hobby
{
public int HobbyId { get; set; }
public string HobbyName { get; set; }
public ICollection<Student> Students { get; set; }
}
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options)
{
}
public DbSet<Student> Students { get; set; }
public DbSet<Class> Classes { get; set; }
public DbSet<Hobby> Hobbies { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Student>().HasKey(c => new { c.StudentId });
modelBuilder.Entity<Class>().HasKey(c => new { c.ClassId });
modelBuilder.Entity<Hobby>().HasKey(h => new { h.HobbyId });
}
}
Here's a simple controller action that uses a single EF Core 5 query statement in the get action to query for students along with their hobbies and classes included:
private readonly AppDbContext _dbContext;
public HomeController(AppDbContext dbContext, ILogger<HomeController> logger)
{
_dbContext = dbContext;
_logger = logger;
}
[HttpGet]
public IActionResult Index()
{
var students = _dbContext.Students
.Include(s => s.Hobbies)
.Include(s => s.Classes)
.ToList();
var filterableStudentListing = new FilterableStudentsListing
{
Students = students,
StudentClasses = students.SelectMany(s => s.Classes).Select(c => new SelectListItem { Text = c.ClassName, Value = c.ClassId.ToString() }).Distinct().ToList(),
StudentHobbies = students.SelectMany(s => s.Hobbies).Select(c => new SelectListItem { Text = c.HobbyName, Value = c.HobbyId.ToString() }).Distinct().ToList()
};
return View(filterableStudentListing);
}
[HttpPost]
public IActionResult Index(FilterableStudentsListing model)
{
// Demonstrates the new EF Core 5 filtered include, but you actually would not want to do this in a real app.
var students = _dbContext.Students
.Include(s => s.Hobbies.Where(h => h.HobbyId == model.SelectedHobbyId))
.Include(s => s.Classes.Where(c => c.ClassId == model.SelectedClassId))
.ToList();
var filterableStudentListing = new FilterableStudentsListing
{
Students = students,
StudentClasses = students.SelectMany(s => s.Classes).Select(c => new SelectListItem { Text = c.ClassName, Value = c.ClassId.ToString() }).Distinct().ToList(),
StudentHobbies = students.SelectMany(s => s.Hobbies).Select(c => new SelectListItem { Text = c.HobbyName, Value = c.HobbyId.ToString() }).Distinct().ToList()
};
return View(filterableStudentListing);
}
Clarifying on my code comment in the post action, the reason why it wouldn't be a good idea to use the EF Core Filtered Include in any real app, is you'd be stripping out possible select options for the user to choose each time you select a filter option to filter the data. In a real app, you would just filter the students in the view model after using the exact same query as in the get action. I just wanted to demo filtered include since its the new hotness in EF Core.
Here's a simple razor view that shows the students with their classes and hobbies:
@model FilterableStudentsListing
@{
ViewData["Title"] = "Home Page";
}
<div class="text-center">
<h1 class="display-4">Example</h1>
</div>
<div>
<form id="form1" method="post">
<select asp-for="SelectedClassId" asp-items="@Model.StudentClasses"> </select>
<select asp-for="SelectedHobbyId" asp-items="@Model.StudentHobbies"></select>
<button type="submit">Filter</button>
</form>
</div>
<div>
@foreach (var student in Model.Students)
{
<h4>@student.StudentName</h4>
<h5>Classes</h5>
<ul>
@foreach (var c in student.Classes)
{
<li>@c.ClassName</li>
}
</ul>
<h5>Hobbies</h5>
<ul>
@foreach (var h in student.Hobbies)
{
<li>@h.HobbyName</li>
}
</ul>
}
Here is the simple model class for the razor view:
public class FilterableStudentsListing
{
public List<Student> Students { get; set; }
public List<SelectListItem> StudentHobbies { get; set; }
public List<SelectListItem> StudentClasses { get; set; }
public int SelectedClassId { get; set; }
public int SelectedHobbyId { get; set; }
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…