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

时间:2013-02-07 01:37:52

标签: c# linq entity-framework linq-to-entities many-to-many

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

我认为需要显示模块列表(如课堂教学)。每个模块属于一个单元,但每个单元可以有许多模块。每个模块可以有多个教师,每个教师可以教授许多模块。有一个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。我的问题是我最难让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中,除了教师之外,我得到了所有内容,然后是教师,并通过将它们插入到集合中来循环播放。

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集合中来循环,但是我得到了所有其他教师。

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

1 个答案:

答案 0 :(得分:1)

我认为https://stackoverflow.com/a/7966436/596146更符合您的要求。我之前没有使用过Entity Framework(仅Fluent NHibernate),但我认为Module应该有ICollection<Instructor>而不是ICollection<ModuleInstructor>,然后告诉EF关于关联表