Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
92 views
in Technique[技术] by (71.8m points)

c# - 2x many-to-many queue from database using Entity Framework Core

This is where I struggle, for example, you have a setup up 2 x many-to-many relationship with Student in the center.

You have ClassID and HobbyID, how do you list all the students (+ all hobbies and classes in the collection inside student object) that have those using Entity Framework Core, and not have to access the database multiple times or to have to queue everything.

Thanks :)


Edit :

This is an example of what I want to achieve with minimum times accessing the database.

After you chose options on dropdowns (available information is ClassID and HobbyID) and click Filter button you should get students that have those. The students should be displayed with the rest of the classes and hobbies they have.

It is not possible to queue it all from single access to DB, how would you do it otherwise to be as effective as you can be.

Sorry if I was unclear. I am trying as hard as I can to explain this. Thank you for being patient! :)

Example design =>

<span>Pick a Hoby</span>
<select>
<option value="1">Climbing</option>
<option value="2">Running</option>
<option value="3">Painting</option>
</select>

<span style="margin-left:20px">Pick a Class</span>
<select >
<option value="1">Art</option>
<option value="2">Mathematics</option>
<option value="3">Geography</option>
</select>

<button>Filter</button>
<br>
<hr>
List of students :
<p>- <b>Mark</b> has <b>Hobbies : </b> Climbing, Painting / <b>Attending Classes : </b>: <b>Art</b>, Geography</p>
<p>- <b>Jhon</b> has <b>Hobbies : </b> Climbing / <b>Attending Classes : </b> Art</p>
<p>- <b>Steven</b> has <b>Hobbies : </b> Climbing, Running / <b>Attending  Classes : </b> Art, Geography, Mathematics</p>
question from:https://stackoverflow.com/questions/65864362/2x-many-to-many-queue-from-database-using-entity-framework-core

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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; }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...