Эффективный способ заполнить запрос Entify Framework связанными объектами? - PullRequest
0 голосов
/ 27 августа 2009

Я использую EF для набора объектов и хочу запросить один тип объекта с соответствующими записями из двух других таблиц. Это для выгрузки запроса в вывод XML. Сначала использовалось объединение, поскольку в исходных данных каждый Сотрудник всегда имел 1+ экземпляров объекта Компьютер (решение № 2 ниже), но это не обязательно так.

Для цели представьте:

  • и Сотрудник объект,
  • каждый сотрудник имеет EmployeeType (несколько фиксированных записей) и
  • каждый сотрудник имеет ноль или более компьютер объекты (обычно 0-3).
  • каждый компьютер принадлежит одному сотруднику, не у каждого сотрудника есть компьютер.
  • У каждого сотрудника есть критерии, на которых основан поиск (например, Отдел ).

Итак, я увидел несколько возможных решений:

  1. Используйте Employee.Computer.Load () внутри цикла, но с 10 000+ строками, что приводит к огромным потерям производительности.

  2. Используйте объединение в запросе, но это исключает всех Сотрудников , у которых нет Компьютера .

  3. Используйте Linq to Entities, , но это, похоже, накладные расходы # 1: при загрузке Компьютер s он попадает в базу данных для каждого Сотрудник .

  4. Используйте второй запрос (все Компьютер s с соответствующими Computer.Employee.Division ), затем в цикле Employee добавьте любой Компьютер для данного сотрудника. При реализации этого я обнаружил, что, просто выполняя второй запрос (с ToList () ), EF заполняет корректные списки Employee.Computer нужными мне объектами. *

Здесь # 4 загружает данные только с 2 попаданиями в базу данных вместо 10k +, и EntityFramework фактически объединяет объекты и создает все отношения.

Мои вопросы :

  • С # 4, является ли факт, что EF заполняет список Employee.Computer , на который я могу положиться? Если да, можете ли вы указать мне документацию?
  • Есть ли лучший способ, чем # 4?

UPDATE : Ну, черт возьми. Извините, но я просто взорвал это. Я сосредоточился на связь с таблицей «Компьютер» и пропустил тот факт, что я было явное Employee.EmployeeTypeReference.Load () без первого тестирования для нуля, так что список «Компьютер» был совершенно не проблема.

Я обнаружил это только при запуске некоторых тестов производительности и добавлении Решение Крейга для смеси. По правде говоря, записи не "сотрудники" и "Компьютеры", но абстракции, и я (условно) включаю каждый поля в выводе XML, но они малы: имя, идентификатор (PK) и идентификатор (FK) плюс INT в таблице «Сотрудник». Итак, я предполагаю, что производительность будет аналогичной, так как EF будет создавать объекты не намного тяжелее, чем проекция.

В любом случае, вот результаты, когда "прошедшее" время было разница до этого запроса и после создания результирующего XML.

  • Случай 1: То же, что и № 2, но с Включить () заявления:

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

    Прошло: 4,96, 5,05

  • Случай 2: используется in-line Load () :

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

    Прошло: 74,62

  • Случай 3: То же, что и # 4, но с Включить () заявления:

    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: используется in-line 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: предложение Крейга о проекциях:

    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 ]

2 голосов
/ 27 августа 2009

Я считаю, что вы можете указать, что отношения могут быть предварительно загружены

Dim employees = From emp in db.Employees.Include("Computer") _
                Select emp
1 голос
/ 27 августа 2009

Решение Роба будет работать (+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 s и просто сохранить полученное дерево, а не вручную конвертировать в XML. Я не пробовал это, но похоже, что это должно работать.

Что касается # 4, да, вы можете положиться на это, но всегда полезно проверить IsLoaded перед вызовом Load().

...