2014-09-02 2 views
12

я в настоящее время этот LINQ/EF-код в моем приложении:Фильтрация включают элементы в LINQ и Entity Framework

var rootCategoryItem = DatabaseContext.Categories 
          .Include("SubCategories") 
          .OrderBy(c => c.CategoryOrder) 
          .Single(c => c.CategoryId == 1); 

Я знаю, в EF вы не можете отфильтровать комплектности еще, и я могу написать некоторые LINQ код отфильтровывать SubCategories, которые не нужны ... но код LINQ преобразуется в ужасающий SQL, который сильно не оптимизирован. Я мог бы также написать сохраненный proc, который делает это (и написать гораздо лучший запрос, чем LINQ), но я действительно хочу использовать чистый EF.

Итак, у меня осталось 2 варианта (если кто-то не видит другие варианты).

Первый является проходным подкатегорий, удалить те, которые не нужны:

 var subCategoriesToFilter = rootCategoryItem.SubCategories.ToList(); 

     for (int i = 0; i < subCategoriesToFilter.Count; i++) 
     { 
      if (subCategoriesToFilter[i].Deleted) 
       rootCategoryItem.SubCategories.Remove(subCategoriesToFilter[i]); 
     } 

Второй вариант будет иметь это, на мой взгляд:

<ul class="treeview ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion ui-widget ui-sortable ui-accordion-content-active"> 
@foreach (var categoryitem in Model.SubCategories.OrderBy(c => c.CategoryOrder)) 
{ 

    @if(!Model.Deleted) 
    { 
     <li class="treelistitem" id="@Model.CategoryId"> 
      <div class="ui-accordion-header ui-state-default ui-corner-all ui-accordion-icons ui-sortable-handle first"> 
      <span class="clickable"> 
       <span class="ui-accordion-header-icon ui-icon treeviewicon treeviewplus"></span> 
       <i class="glyphicon glyphicon-folder-open rightfolderpadding"></i><span class="categoryname">@Model.CategoryName</span> 
      </span> 
      </div> 
      </li> 
    } 
} 
</ul> 

Вне 2, какой из них был бы лучшим вариантом? Или есть другой вариант, который мне не хватает?

Решение

OK, Servy является в значительной степени правильно, я должен был изменить свой ответ, чтобы сделать его работу:

 var rootCategoryItem = DatabaseContext.Categories 
      .OrderBy(c => c.CategoryId) 
      .ToList().Select(c => new Category() 
      { 
       SubCategories = c.SubCategories.Where(sub => !sub.Deleted).ToList(), //make sure only undeleted subcategories are returned 
       CategoryId = c.CategoryId, 
       CategoryName = c.CategoryName, 
       Category_ParentID = c.Category_ParentID, 
       CategoryOrder = c.CategoryOrder, 
       Parent_Category = c.Parent_Category, 
       Deleted = c.Deleted 
      }).Single(c => c.CategoryId == 1); 

У меня было несколько ошибок, пытаясь получить решение Servy к работе:

The entity or complex type '.Category' cannot be constructed in a LINQ to Entities query

Cannot implicitly convert type to System.Collections.Generic.ICollection. An explicit conversion exists (are you missing a cast?)

Все это было разрешено путем добавления .ToList() перед методом Select().

+6

У вас есть правильное решение? Этот ToList(), который вы добавили, будет загружать всю таблицу категорий из базы данных. –

+0

точно, что @JoshMouch сказал. он будет работать, потому что вы возвращаете всю таблицу, а затем используете LinqToEntities для результата для дальнейшего ее фильтрации. Я также пытаюсь сохранить загрузку базы данных :) сообщит, если я найду лучший способ –

ответ

17

Пока вы не можете отфильтровать коллекцию, включенную через Include, вы можете использовать Select и проецировать эту коллекцию в отфильтрованную коллекцию.

var rootCategoryItem = DatabaseContext.Categories 
    .OrderBy(c => c.CategoryOrder) 
    .Select(c => new Category() 
    { 
     SubCategories = c.SubCategories.Where(sub => !sub.Deleted) 
      .OrderBy(sub => sub.CategoryOrder), 
     c.CategoryId, 
     c.CategoryName, 
     //include any other fields needed here 
    }) 
    .Single(c => c.CategoryId == 1); 
+2

Мне действительно нравится это решение, однако, я получаю сообщение об ошибке в этой строке: SubCategories = c.SubCategories.Where (sub => ! sub.Deleted) .OrderBy (sub => sub.CategoryOrder), Невозможно неявно преобразовать тип 'System.Linq.IOrderedEnumerable ' в 'System.Collections.Generic.ICollection '. Явное преобразование существует (вы пропускаете листинг?) – binks

+0

@binks Затем материализуйте 'IEnumerable' в' ICollection'. – Servy

+1

Хм ... Я пробовал литье (ICollection ) c.SubCategories.Where (суб =>! Sub.Deleted) .OrderBy (суб => sub.CategoryOrder), Хотя это работает, я тогда получить новая ошибка: Объект или сложный тип '.Category' не может быть сконструирован в запросе LINQ to Entities. – binks

0

Ваши проблемы здесь касаются представления (показаны только категории, не связанные с удалением). Это будет указывать на способ 2 как ваш лучший выбор.

ОДНАКО, Я подозреваю, что вам часто нужно использовать неиспользуемые категории в вашей системе. Это указывает на то, что у вас должна быть функция, которая неизменно возвращает неизведанные категории для вашего использования в любом месте, где она вам нужна.

По этой причине я бы рекомендовал метод 1.

3

Я Recon этот путь выглядит более чище и короче. Не определенно о воздействии базы данных

var rootCategoryItem = DatabaseContext.Categories.SingleOrDefault(); 
if (rootCategoryItem == null) return null; 
{ 
    rootCategoryItem.Items = rootCategoryItem ?.Items.Where(x => !x.IsDeleted).ToList(); 
    return rootCategoryItem; 
    } 
Смежные вопросы