0

我将 EF 用于一组对象,并希望使用其他两个表中的匹配条目查询一种类型的对象。这是将查询转储到 XML 输出中。起初,使用连接,因为在原始数据中每个Employee总是有 1+ 个Computer对象实例(下面的解决方案 #2),但这不一定是真的。

为了这里的目的,想象一下:

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

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

  1. 在循环中使用Employee.Computer.Load(),但有 10,000 多行,这会导致巨大的性能损失。

  2. 在查询中使用连接,但这会忽略所有没有Computer的员工

  3. 使用 Linq to Entities,但这似乎有 #1 的开销:加载Computer时,它会访问 EACH Employee的数据库。

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

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

我的问题

  • 对于 #4,EF 填充Employee.Computer列表的事实是我可以依赖的吗?如果是这样,你能指出我的文档吗?
  • 有比#4更好的方法吗?

更新:好吧,臭虫。对不起,我只是把它搞砸了。我专注于与“计算机”表的关系,并错过了这样一个事实,即我有一个明确的Employee.EmployeeTypeReference.Load()没有第一次测试空值,所以“计算机”列表完全没有问题。

我只是在运行一些性能测试并将 Craig 的解决方案添加到组合中时才发现这一点。事实上,记录不是“员工”和“计算机”而是抽象,我(有条件地)在 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:使用内联Load()

    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:使用内联Load()

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

    已过:74.20

  • 案例 5:使用 *Include("EmployeeType") 和单独的 "Computer" 查询,让 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:Craig 的预测建议:

    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 修复快得多。[在“窄”表上进行有限测试]

4

2 回答 2

2

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

Dim employees = From emp in db.Employees.Include("Computer") _
                Select emp
于 2009-08-27T00:06:00.607 回答
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
                          }
        };

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

您甚至可以选择 into XElements 并保存生成的树,而不是手动转换为 XML。我还没有尝试过,但它似乎应该可以工作。

关于#4,是的,你可以依靠这个,但IsLoaded在调用之前检查总是好的Load()

于 2009-08-27T13:17:09.783 回答