1

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

我有一个需要显示模块列表的视图(如课堂教学)。每个 Module 属于一个 Unit,但每个 Unit 可以有多个 Module。每个模块可以有多个讲师,每个讲师可以教授多个模块。有一个 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、Instructor(s)。我的问题是我最难让 Linq 获得 Instructors,我所能检索到的只是 ModuleInstructors,它没有加载任何 Instructor 信息。

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 之外的所有内容,然后是 Instructors,它们分别并通过将它们插入集合中进行循环。

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 个模块显示。

但是,我认为必须有一种方法可以在一个数据库调用中做到这一点。这是我的新解决方案。我仍然循环将 Instructors 插入到 Modules 对象的 Instructor 集合中,但我得到了所有 Instructors 以及其他所有内容。

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 上的这种特殊情况似乎没有答案,我正在尝试继续。这个问题包括我所有的尝试,如果我没有收到回复,我将用我自己的最终解决方案来回答。

4

1 回答 1

1

I think https://stackoverflow.com/a/7966436/596146 is more what you're looking for. I've not used Entity Framework before (only Fluent NHibernate) but I think that Module should have an ICollection<Instructor> rather than an ICollection<ModuleInstructor> and then you tell EF about the assocation table.

于 2013-02-15T05:26:42.843 回答