1
votes

C # Optimisation de la requête Linq: mise à jour avec where

Comment optimiser la requête suivante? Nous examinons les requêtes de structure d'entité dans la base de données et essayons d'apprendre.

        using (var context = new DataDbContext())
        {
            var query = (from u in content.Parents
                         where u.Children.Any(y = y.Age > 13)
                         select u);

            foreach (var parent in query.ToList())
            {
                foreach (var children in owner.Children)
                {
                    children.IsTeenager= true;
                }
            }
            context.SaveChanges();
        }


5 commentaires

Qu'est-ce qui vous fait penser qu'il doit être optimisé?


Vous voudrez peut-être vérifier l'âge dans la boucle interne; cela pourrait ne pas mettre à jour ce que vous voulez.


Cela semble assez simple. Vous ne pouvez pas mettre à jour les enregistrements sans interroger d'abord. La seule alternative serait de sauter l'entité et d'exécuter directement une instruction de mise à jour SQL.


La logique est un peu étrange. Définir les animaux de compagnie de tous les propriétaires sur IsTeenager = true si l'un animal qu'ils possèdent a plus de 13 ans?


Cela ressemble à un problème courant de N + 1 Entity Framework. Il est également étrange que vous définissiez la propriété IsTeenager , qui devrait être calculée en fonction de la propriété Age .


5 Réponses :


1
votes

Il n'y a pas grand chose à optimiser, vous pouvez réduire le code. La logique est un peu étrange

using (var context = new DataDbContext())
{
    var parents = content.Parents.Where(o => o.Children.Any(x => x.Age > 13));
    foreach (var child in parents.SelectMany(o => o.Children))
    {
        children.IsTeenager= true;
    }

    context.SaveChanges();
}

ou

using (var context = new DataDbContext())
{
    foreach (var child in content.Parents.Where(o => o.Children.Any(x => x.Age > 13)).SelectMany(o => o.Children))
    {
        children.IsTeenager= true;
    }

    context.SaveChanges();
}


0 commentaires

3
votes

Il n'y a pas de moyen formidable de simplifier ce que vous faites ici. EF a tendance à vous pousser à lire un tas de données à partir du magasin de données, à mettre à jour les entités localement, puis à réécrire ces modifications. C'est évidemment assez lent. Cependant, ce que vous essayez de faire ici est d'obtenir tous les enfants et de définir la propriété IsTeenager afin que vous ne vous souciez même pas de l'objet Parent , vous pouvez simplement le faire :

context.Database.ExecuteSqlCommand("UPDATE Children SET IsTeenager = 1 WHERE Age < 13");

Bien sûr, il existe un moyen beaucoup plus simple de le faire en utilisant du SQL brut. Par exemple:

var children = context.Children.Where(c => c.Age < 13);

foreach(var child in children)
{
    child.IsTeenager = true;
}

context.SaveChanges();


0 commentaires

0
votes

J'ai vu ce qui suit dans vos commentaires plus tôt (a depuis été supprimé):

c'était une question d'entretien d'embauche que j'avais eue il y a quelques mois, je ne suis même pas sûr

Si je devais poser cette question à quelqu'un lors d'un entretien d'embauche, j'espère qu'il m'informera de ce qui suit:


  1. L'âge et IsTeenager doivent dépendre de chaque entité individuelle et ne pas être calculés sur une collection.
  2. Vous devez vous assurer que la date de naissance de la personne / de l'enfant est conservée et non son âge. Sinon, vous ne savez pas quand l'âge change pour une entité car l'âge n'est valide qu'à la date et à l'heure auxquelles l'entité est capturée dans le système.
  3. IsTeeneger ne doit pas être persistant, il doit être dérivé en fonction de la valeur Date de naissance. S'il n'est vérifié que dans le code c # et que vous n'avez pas à faire de requête, faites-en un champ de lecture de modèle uniquement qui n'est pas mappé dans Sql. Si vous avez besoin d'interroger contre elle, faites-en une colonne calculée est le schéma de base de données. La même chose devrait être faite pour Age !
  4. Le parent et l'enfant pourraient mieux être modélisés en tant que personne avec une relation un-à-plusieurs récursive Enfants. De cette façon, vous pouvez également faire des grands parents qui ont des enfants qui ont des enfants.

Modèle uniquement

public class PersonModel
{
    public DateTime BirthDate { get; set; }
    public int Age { get; set; } // should be computed and mapped from sql
    public bool IsTeenager { get; set; } // should be computed and mapped from sql
}

Mappé sur une colonne calculée

public class PersonModel
{
    public DateTime BirthDate { get; set; }
    public int Age
    {
        get
        {
            var today = DateTime.Today;
            // Calculate the age.
            var age = today.Year - BirthDate.Year;
            // Go back to the year the person was born in case of a leap year
            if (BirthDate > today.AddYears(-age)) age--;
            return age;
        }
    }
    public bool IsTeenager
    {
        get
        {
            return Age >= 13 && Age < 20;
        }
    }
}


5 commentaires

La logique pour IsTeenager telle qu'elle est écrite n'est pas que «l'enfant» a plus de 13 ans, cela dépend aussi de l'âge de ses frères et sœurs.


@CharlesMager Ouais, c'est certainement faux aussi.


@DavidG Je suppose qu'en tant que question d'entretien d'embauche, ils recherchent simplement que vous repériez le problème sélectionné N + 1. Mais qui sait?!


@CharlesMager - Si je posais cette question, je serais plus heureux si quelqu'un pouvait signaler toutes les erreurs de logique et de conception au lieu de simplement regarder le N + 1. Mais c'est juste moi.


@CharlesMager Ouais, je commence à penser que c'était une mauvaise idée de répondre moi-même à cette question.



1
votes
content.Children.Where(o => !o.IsTeenager && o.Age >= 13 && o.Age <= 19)

0 commentaires

0
votes

Lorsque vous souhaitez mettre à jour des données à l'aide d'Entity Framework, vous devez d'abord les récupérer dans la mémoire (soit par chargement hâtif, soit par chargement différé), puis les modifier.
Parfois, cela pose un problème N + 1. Cela signifie N + 1 appels au serveur de base de données. Et si vous utilisez un hébergement basé sur le cloud comme Azure ou AWS, vous êtes facturé par demande. Ce serait donc aussi coûteux financièrement.

Dans votre cas, je préférerais faire cela dans Yes Entity Framework, mais en utilisant la procédure stockée. Les requêtes simples sont utilisées mais je ne vous recommanderai pas de le faire de cette manière.

L'utilisation de la procédure stockée vous évitera d'apporter des données dans la mémoire et obtiendra le résultat en utilisant 1 appel de base de données.


0 commentaires