web-dev-qa-db-fra.com

Filtrage d'éléments inclus dans LINQ et Entity Framework

J'ai actuellement ce code LINQ/EF dans mon application:

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

Je sais qu'en EF, vous ne pouvez pas encore filtrer les éléments inclus, et je peux écrire du code LINQ pour filtrer les sous-catégories inutiles ... mais le code LINQ est converti en un SQL horrible qui est hautement non optimisé. Je pourrais aussi écrire un proc stocké qui fait cela (et écrire une requête bien meilleure que LINQ), mais je veux vraiment utiliser EF pur.

Il me reste donc 2 options (sauf si quelqu'un peut voir d'autres options).

La première consiste à parcourir les sous-catégories et à supprimer celles qui ne sont pas nécessaires:

        var subCategoriesToFilter = rootCategoryItem.SubCategories.ToList();

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

La deuxième option serait d'avoir ceci à mon avis:

<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>

Parmi les 2, lequel serait la meilleure option? Ou y a-t-il une autre option qui me manque?

La solution

OK, Servy est à peu près correct, j'ai dû modifier sa réponse pour que cela fonctionne:

        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);

J'ai eu plusieurs erreurs en essayant de faire fonctionner la solution de Servy:

L'entité ou le type complexe '.Category' ne peut pas être construit dans une requête LINQ to Entities

Impossible de convertir implicitement le type en System.Collections.Generic.ICollection. Une conversion explicite existe (manque-t-il un casting?)

Tout cela a été résolu en ajoutant .ToList () avant la méthode Select ().

16
binks

Bien que vous ne puissiez pas filtrer une collection incluse via Include, vous pouvez utiliser Select et projeter cette collection dans une collection filtrée.

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);
19
Servy

Je reconnais que cette voie est plus propre et plus courte. Pas sûr de l'impact de la base de données 

 var rootCategoryItem = DatabaseContext.Categories.SingleOrDefault();
 if (rootCategoryItem == null) return null;
 {
   rootCategoryItem.Items = rootCategoryItem ?.Items.Where(x => !x.IsDeleted).ToList();
   return rootCategoryItem;
  }
3
Aleksei

Vos préoccupations ici sont une préoccupation de présentation (montrant uniquement les catégories non supprimées). Cela indiquerait la méthode 2 comme votre meilleur choix.

CEPENDANT, je suppose que vous avez souvent besoin d’utiliser des catégories non supprimées dans votre système. Cela indiquerait que vous devriez avoir une fonction qui renvoie systématiquement les catégories non supprimées pour votre utilisation, où que vous soyez.

Pour cette raison, je recommanderais la méthode 1. 

0
Chuck Buford