Упорядочить свойства навигации при использовании методов Include и / или Select с EF 4.1 Code-First? - PullRequest
8 голосов
/ 23 сентября 2011

Это второй шаг вопроса, объясненного здесь: EF 4.1 code-first: Как загрузить связанные данные (parent-child-внучка)? . Под руководством @ Slauma я успешно получил данные с помощью этого подхода:

var model = DbContext.SitePages
    .Where(p => p.ParentId == null && p.Level == 1)
    .OrderBy(p => p.Order) // ordering parent 
    .ToList();

foreach (var child in model) { // loading children
    DbContext.Entry(child)
        .Collection(t => t.Children)
        .Query()
        .OrderBy(t => t.Order) // ordering children
        .Load();

    foreach (var grand in child.Children) { // loading grandchildren
        DbContext.Entry(grand)
            .Collection(t => t.Children)
            .Query()
            .OrderBy(t => t.Order) // ordering grandchildren 
            .Load();
    }
}

Хотя этот подход работает, он отправляет множество запросов в базу данных, и я ищу способ сделать все это одним запросом. Под руководством @ Slauma (поясняется в ответе по ссылке выше) я изменил запрос на этот:

var model2 = DbContext.SitePages
    .Where(p => p.ParentId == null && p.Level == 1)
    .OrderBy(p => p.Order)
    .Include(p => p.Children // Children: how to order theme???
        .Select(c => c.Children) // Grandchildren: how to order them???
    ).ToList();

Теперь, как я могу заказать детей (и внуков) при их выборе (как показано в первом примере кода выше)?

Ответы [ 2 ]

23 голосов
/ 23 сентября 2011

К сожалению, стремительная загрузка (Include) не поддерживает фильтрацию или сортировку загруженных дочерних коллекций. Есть три варианта достижения желаемого:

  • Несколько обращений к базе данных с явной сортированной загрузкой. Это первый фрагмент кода в вашем вопросе. Имейте в виду, что множественные обращения не обязательно являются плохими и что Include и вложенные Include могут привести к огромному увеличению числа передаваемых данных между базой данных и клиентом .

  • Используйте активную загрузку с помощью Include или Include(....Select(....)) и сортируйте данные в памяти после их загрузки:

    var model2 = DbContext.SitePages
        .Where(p => p.ParentId == null && p.Level == 1)
        .OrderBy(p => p.Order)
        .Include(p => p.Children.Select(c => c.Children))
        .ToList();
    
    foreach (var parent in model2)
    {
        parent.Children = parent.Children.OrderBy(c => c.Order).ToList();
        foreach (var child in parent.Children)
            child.Children = child.Children.OrderBy(cc => cc.Order).ToList();
    }
    
  • Используйте проекцию:

    var model2 = DbContext.SitePages
        .Where(p => p.ParentId == null && p.Level == 1)
        .OrderBy(p => p.Order)
        .Select(p => new
        {
            Parent = p,
            Children = p.Children.OrderBy(c => c.Order)
                .Select(c => new
                {
                    Child = c,
                    Children = c.Children.OrderBy(cc => cc.Order)
                })
        })
        .ToList() // don't remove that!
        .Select(a => a.Parent)
        .ToList();
    

Это только одна поездка туда и обратно и работает, если вы не отключите отслеживание изменений (не используйте .AsNoTracking() в этом запросе). Все объекты в этой проекции должны быть загружены в контекст (причина, по которой необходим первый ToList()), и контекст будет корректно связывать свойства навигации (что называется "Диапазон отношений" ) .

0 голосов
/ 23 сентября 2011

Пытались ли вы следовать?

...