在一个数据库操作中使用结构[a]1:n[B]n:1[C]1:n[D]进行查询

本文关键字:查询 结构 一个 数据库 操作 | 更新日期: 2023-09-27 17:58:10

我正在现有SQL Server数据库上使用实体框架作为ORM构建一个ASP.NET MVC 4应用程序这是模型优先,而不是代码优先我是LINQ to Entity的新手。最近我遇到了一些问题,我很难在网上找到任何帮助。我现在已经解决了两次。这是我的故事。

我有一个视图,需要显示模块列表(如在课堂教学中)。每个模块属于一个单元,但每个单元可以有多个模块。每个模块可以有多个讲师,每个讲师可以教授多个模块。存在一个ModuleInstructor表来联接该关系。

Unit           Module        ModuleInstructor         Instructor
========  1:N  ========  N:1 ==================  1:N  ============
UnitID         ModuleID      ModuleInsturctorID       InstructorID
UnitName       UnitID        ModuleID                 InstructorName
               StartDate     InsturctorID

EntityFramework为对象提供了以下内容[为简洁起见,修剪了不必要的属性]:

public partial class Unit
{
    public int UnitID { get; set; }
    public string UnitName { get; set; }
    public virtual ICollection<Module> Module { get; set; }
}
public partial class Module
{
    public Module()
    {
        this.ModuleInstructor = new HashSet<ModuleInstructor>();
        this.Record = new HashSet<Record>();
    }
    public int ModuleID { get; set; }
    public Nullable<int> UnitID { get; set; }
    public Nullable<System.DateTime> ModuleDate { get; set; }
    public virtual Unit Unit { get; set; }
    public virtual ICollection<ModuleInstructor> ModuleInstructor { get; set; }
}
public partial class ModuleInstructor
{
    public int ModuleInstructorID { get; set; }
    public Nullable<int> InstructorID { get; set; }
    public Nullable<int> ModuleID { get; set; }
    public Instructor Instructor { get; set; }
    public virtual Module Module { get; set; }
}
public partial class Instructor
{
    public Instructor()
    {
        this.ModuleInstructor = new HashSet<ModuleInstructor>();
    }
    public int InstructorID { get; set; }
    public string InstructorName { get; set; }
    public virtual ICollection<ModuleInstructor> ModuleInstructor { get; set; }
}

无论如何,我想在逗号分隔的列表中显示:UnitName、ModuleDate、讲师。我的问题是,我最难让Linq获得讲师,我只能检索到没有加载任何讲师信息的ModuleInstructors。

private Entities db = new Entities();
public ActionResult Index(int p = 1)
{
    int pageSize = 20;
    var modules = db.Modules
      .Include(m => m.Unit)
      .Include(m => m.ModuleInstructor)
      .Include(m => m.ModuleInstructor.Instructor)  //Doesn't work
      .OrderByDescending(m => m.ModuleStartDate)
      .Skip(pageSize * (p - 1))
      .Take(pageSize);
    return View(modules);
}

我的第一个解决方案是修改Module类以添加另一个集合:

public partial class Module
{
    public Module()
    {
        this.ModuleInstructor = new HashSet<ModuleInstructor>();
        this.Record = new HashSet<Record>();
    }
    public int ModuleID { get; set; }
    public Nullable<int> UnitID { get; set; }
    public Nullable<System.DateTime> ModuleDate { get; set; }
    public virtual Unit Unit { get; set; }
    public virtual ICollection<Instructor> Instructor { get; set; } //Added
    public virtual ICollection<ModuleInstructor> ModuleInstructor { get; set; }
}

在ModuleController中,我得到了除Instructors之外的所有东西,然后分别获得了Instructorss,并将它们循环插入集合中。

private Entities db = new Entities();
public ActionResult Index(int p = 1)
{
    int pageSize = 20;
    var modules = db.Modules
      .Include(m => m.Unit)
      .Include(m => m.ModuleInstructor)
      .OrderByDescending(m => m.ModuleStartDate)
      .Skip(pageSize * (p - 1))
      .Take(pageSize);
    var instructors = db.Instructors.ToList();
    foreach (var m in modules)
    {
        m.Instructor = new List<Instructor>();
        foreach (var mi in m.ModuleInstructor)
        {
            m.Instructor.Add(instructors
              .Where(i => i.InstructorID == mi.InstructorID).SingleOrDefault());
        }
    }
    return View(modules);
}

根据LinqPad的说法,这需要两个SQL语句。还不错。服务器上也没有太大的压力,因为我没有太多的讲师,而且一次只显示20个模块。

然而,我认为一定有一种方法可以在一个数据库调用中实现这一点。这是我的新解决方案。我仍然循环将讲师插入Modules对象的讲师集合,但我获得了所有讲师和其他所有内容。

public ActionResult Index(int p = 1)
{
    int pageSize = 20;
    var modules = db.Modules
      .Include(m => m.Unit)
      .Include(m => m.ModuleInstructor)
      .Include(m => m.ModuleInstructor.Select(mi => mi.Instructor)) //New 'trick'
      .OrderByDescending(m => m.ModuleStartDate)
      .Skip(pageSize * (p - 1))
      .Take(pageSize);
    foreach (var m in modules)
    {
        m.Instructor = new List<Instructor>();
        foreach (var mi in m.ModuleInstructor)
        {
            //Adds from the ModuleInstructor instead of pairing from another list
            m.Instructor.Add(mi.Instructor);
        }
    }
    return View(modules);
}

根据LinqPad的说法,它只需要SQL语句,而且它的执行速度比第一种方法稍快。

老实说,我很难准确地想象这是如何运作的以及为什么,但确实如此。我很高兴我坚持了这个问题,又回来了好几次,直到我得到了"刚刚好"的

我的一个问题是:这是最好的方法还是有更好的方法

澄清:我无法更改数据库表,也对更改类不感兴趣。我在问是否有更好的方法可以对数据结构进行这种操作。对于StackOverflow上的这种特殊情况,似乎没有答案,我正在努力解决这个问题。这个问题包括我所有的尝试,如果我没有得到回应,我将用我自己的最终解决方案来回答。

在一个数据库操作中使用结构[a]1:n[B]n:1[C]1:n[D]进行查询

我认为https://stackoverflow.com/a/7966436/596146更多的是你想要的。我以前没有使用过实体框架(只有Fluent NHibernate),但我认为Module应该有一个ICollection<Instructor>而不是ICollection<ModuleInstructor>,然后你告诉EF关于关联表的信息。