使用相关对象填充实体框架查询的有效方法?

时间:2009-08-26 22:16:41

标签: c# entity-framework entity-relationship

我正在将EF用于一组对象,并希望使用来自另外两个表的匹配条目来查询一种类型的对象。这是将查询转储到XML输出中。首先,使用连接,因为在原始数据中,每个 Employee 总是有1个以上计算机对象的实例(下面的解决方案#2),但它不一定是真的。< / p>

为此目的,想象一下:

  • 员工对象,
  • 每个员工都有 EmployeeType (一些固定条目)和
  • 每个员工都有或更多计算机对象(通常为0-3)。
  • 每台计算机属于一个员工,并非所有员工都有计算机。
  • 每个员工都有一个搜索所依据的标准(例如 Division )。

所以我看到了几种可能的解决方案:

  1. 在循环中使用 Employee.Computer.Load(),但行数超过10,000行会导致性能损失。

  2. 在查询中使用 join ,但这会遗漏所有没有计算机员工

  3. 使用Linq to Entities,但这似乎有#1的开销:当加载计算机时它会触及数据库以获取每个员工

  4. 使用第二个查询(所有计算机 Computer.Employee.Division 匹配),然后在 Employee 循环中,为给定的员工添加任何计算机。实现这一点时,我发现只运行第二个查询(使用 ToList()),EF会使用我需要的对象填充正确的 Employee.Computer 列表。

  5. 这里,#4仅使用2个数据库命中而不是10k +加载数据,EntityFramework实际上整理了对象并创建了所有关系。

    我的问题

    • #4,是因为EF填充了 Employee.Computer 列表,我可以依赖它吗?如果是这样,你能指点我的文件吗?
    • 有比#4更好的方法吗?

    更新 : 好吧,开玩笑。对不起,我只是吹了它。我专注于 与“电脑”表的关系,错过了我的事实 有一个明确的 Employee.EmployeeTypeReference.Load()没有第一次测试 对于null,所以“计算机”列表是一个完整的非问题。

    我在运行一些性能测试和添加时才发现这一点 克雷格的混合解决方案。事实上,记录不是“员工” 和“计算机”但抽象,我(有条件地)包括每一个 XML输出中的字段,但它们很小:名称,ID(PK)和ID(FK) 加上“员工”表中的INT。所以,我的推测是 由于EF不会创建对象,因此性能会相似 比投影重得多。

    无论如何,这是“经过”时间的结果 此查询之前和创建结果XML之后的差异。

    • 案例1:与#2相同,但使用 Include()语句:

      list = ve.Employee.Include("Computer").Include("EmployeeType").Where(e => e.Division.ID == divId).OrderBy(e => e.Name);

      经历:4.96,5.05

    • 案例2:使用内联加载()

      list = ve.Employee.Where(e => e.Division.ID == divId).OrderBy(e => e.Name);

      经历:74.62

    • 案例3:与#4相同,但使用 Include()语句:

      list = from e in ve.Employee.Include("Computer").Include("EmployeeType") where e.Division.ID == divId orderby e.Name select e;

      经历:4.91,5.47

    • 案例4:使用内联加载()

      list = from e in ve.Employee where e.Division.ID == divId orderby e.Name select e;

      经历:74.20

    • 案例5:使用* Include(“EmployeeType”)并单独“计算机”查询,让EF关联:

      elist = ve.Employee.Include("EmployeeType").Where(te => te.Division.ID == divId).OrderBy(e => e.Name).ToList(); alist = ve.Alias.Where(a => a.Employee.Division.ID == divId).ToList();

      经历:4.50,4.02

    • 案例6:克雷格对预测的建议:

      elist = from te in ve.ThesaurusEntry where te.Division.ID==divID orderby te.Name select new { ID = te.ID, Name = te.Name, Type = te.EmployeeType.Name, Freq = te.Frequency, Aliases = from a in te.Alias select new { ID = a.ID, Name = a.Name } };

      经历:0.73,1.25

    结论

    Load()价格昂贵,因此请使用 Include()或至少使用 IsLoaded进行测试

    投影有点乏味,但明显快于EF修复。 [对“窄”表进行有限测试]

2 个答案:

答案 0 :(得分:2)

我相信你可以表明关系可以预先加载

Dim employees = From emp in db.Employees.Include("Computer") _
                Select emp

答案 1 :(得分:1)

Rob的解决方案可以正常工作(+1),但如果您不需要所有字段,例如员工和计算机,我强烈建议投影:

var q = from e in Context.Employees
        where e.Division.Id = divisionId
        select new
        {
            Name = e.Name,
            EmployeeType = e.EmployeeType.Description,
            ComputerIds = from c in e.Computers
                          select new 
                          {
                              Id = c.Id
                          }
        };

在这里,您可以在一个查询中获得所需的一切,但不再是:不会返回您不需要的所有字段。

您甚至可以选择XElement并只保存生成的树而不是手动转换为XML。我没有试过这个,但它似乎应该有效。

关于#4,是的,你可以依赖于此,但在致电IsLoaded之前检查Load()总是好的。