11
votes

Filtrer les propriétés de navigation dans le code EF en premier

J'utilise le code d'abord dans ef. Disons que j'ai deux entités: xxx pré>

mon dbcontext est quelque chose comme ceci: p> xxx pré>

Je fais cela afin que chaque utilisateur puisse seulement voir Ses fermes, et je n'ai pas besoin d'appeler le lieu où sur chaque requête à la DB. P>

Maintenant, je veux filtrer tous les fruits d'une ferme, j'ai essayé ceci (en classe agricole): xxx pré>

mais la requête générée n'inclut pas la clause WHERE, qui est très importante car j'ai des dizaines de milliers de lignes et que cela n'est pas efficace pour les charger tous et les filtrer quand Ce sont des objets. P>

J'ai lu que les propriétés chargées paresseuses sont remplies la première fois qu'elles sont accessibles, mais ils lisent toutes les données, aucun filtre ne peut être appliqué que si vous faites quelque chose comme ceci: P>

from fruits in db.Fruits where fruit .... select fruit


0 commentaires

3 Réponses :


9
votes

Malheureusement, je pense que toute approche que vous pourriez prendre devrait impliquer de violer le contexte, pas seulement de l'entité. Comme vous l'avez vu, vous ne pouvez pas filtrer directement une propriété de navigation, car il s'agit d'un Icollection et non d'un iquéryable , donc il est donc chargé de tous À la fois avant d'avoir une chance d'appliquer des filtres.

Une chose que vous pourriez éventuellement faire est de créer une propriété non mappée dans votre entité de ferme pour contenir la liste des fruits filtrés: < Pré> xxx

puis dans votre contexte / référentiel, ajoutez une méthode pour charger une entité entité et population filtréfruits avec les données souhaitées: xxx

qui devrait remplir myfarm.filteredfruits avec seulement la collection filtrée, vous pouvez donc l'utiliser comme vous le souhaitez dans votre entité. Cependant, je n'ai jamais essayé cette approche moi-même, alors il peut y avoir des pièges que je ne pense pas. Un inconvénient majeur est qu'il ne fonctionnerait que avec ferme Sous vous chargez d'utiliser cette méthode, et non avec des requêtes générales de Linq que vous effectuez sur le mydbcontext.farms Dataset.

Tout ce qui a dit, je pense que le fait que vous essayez de faire cela pourrait être un signe que vous mettez trop de logique commerciale dans votre classe d'entité, quand cela pourrait bien être meilleur dans une couche différente. Beaucoup de temps, il vaut mieux traiter des entités essentiellement comme des réceptacles pour le contenu d'un enregistrement de base de données et laisser tout le filtrage / traitement au référentiel ou où vit votre entreprise / logique. Je ne sais pas quel type d'application vous travaillez. Je ne peux donc pas vraiment proposer des conseils spécifiques, mais c'est quelque chose à penser.

Une approche très courante si vous décidez de déplacer des choses L'entité ferme est d'utiliser la projection: xxx

... puis utilisez les objets anonymes générés pour tout ce que vous voulez faire, plutôt que de Essayer d'ajouter des données supplémentaires aux entités agricole elles-mêmes.


1 commentaires

Merci Jeremy, j'ai décidé de suivre vos conseils et de laisser les responsabilités de filtrage / traitement dans ma classe de contexte. Cela a du sens parce que je n'ai besoin que du filtrage pour une de mes entités, mais ce serait lourd si j'en avais besoin pour plusieurs entités, ne pensez-vous pas? Le contexte serait renseigné avec des méthodes de requête et de remplissage des entités. Je ne pense pas que cela brise le principe de responsabilité unique, mais cela semble plutôt étrange, n'est-ce pas?



3
votes

Juste pensé que je voudrais ajouter une autre solution à ce après avoir passé quelque temps à essayer d'ajouter des principes DDD à premiers modèles de code. Après avoir cherché un certain temps que je trouve une solution comme celle ci-dessous qui fonctionne pour moi.

public class FruitFarmContext : DbContext
{
    public DbSet<Farm> Farms { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Farm>().HasMany(Farm.FruitsExpression).WithMany();
    }
}

public class Farm
{
    public int Id { get; set; }
    protected virtual ICollection<Fruit> Fruits  { get; set; }
    public static Expression<Func<Farm, ICollection<Fruit>>> FruitsExpression = x => x.Fruits;

    public IEnumerable<Fruit> FilteredFruits
    {
        get
        {
            //Apply any filter you want here on the fruits collection
            return Fruits.Where(x => true);
        }
    }
}

public class Fruit
{
    public int Id { get; set; }

}


1 commentaires

Y a-t-il une implication avec cette solution?



3
votes

Chargement paresseux ne prend pas en charge le filtrage; Utilisez Chargement explicite filtré à la place:

Farm farm = (
    from farm in dbContext.Farms
    where farm.Owner == someOwner
    select new {
        Farm = farm,
        Fruit = dbContext.Fruit.Where(fruit => fruit.IsRipe) // Causes Farm.Fruit to be eager loaded
    }).Single().Farm;


0 commentaires