À propos des entités, DTO, ORM et Lazy Load

Le paradigme orienté objet est la norme pour les logiciels d'application. SGBD relationnel - une norme pour le stockage de données dans un logiciel d'application. Oui, vous pouvez écrire dans Haskell et stocker des données exclusivement dans ClickHouse. Mais il s'agit du courant dominant.

ORM vous permet de tirer un hibou sur un globe pour prétendre qu'il n'y a pas de SGBDR et que les données sont stockées dans un modèle d'objet qui convient mieux à la POO. Il reste un "petit" problème de ce type - cette abstraction, comme beaucoup d'autres, "coule". Dans le modèle d'objet, il existe un lien vers un autre objet dans la clé étrangère et l'ID de la base de données. Au moment de la matérialisation de l'entité, nous sommes confrontés à un choix:

  1. Téléchargez tout et tombez de la mémoire / timeout
  2. Indiquez explicitement les dépendances que nous voulons télécharger et celles qui ne le font pas, et violez le principe de ne pas demander
  3. Charger les dépendances implicitement à la demande à l'aide de Lazy Load et obtenir des problèmes de performances quelque part dans le code appelé

Quel type de jambe faut-il couper: gauche ou droite?

TLDR Lazy Load n'est pas si mauvais s'il est utilisé uniquement pour l'écriture et n'est pas utilisé lors de la lecture. Mais tout n'est pas si simple et il y a beaucoup de nuances.

Au fil du temps, je suis arrivé à la conclusion que Lazy Load et / ou la dépendance des entités à la mise en œuvre de l'ORM est le moindre mal dans certaines conditions.

Dans le sous-système de lecture, toujours en lecture seule DTO


Dans 90% des cas, des problèmes avec Lazy Load surviennent précisément lors de la lecture. Nous obtenons la liste des entités, la parcourons et bouclons et commençons à sélectionner toutes les données nécessaires. Nous obtenons un arbre de requêtes dans la base de données. Dans ce cas, la plupart du temps, la seule chose à faire est d'obtenir les données, de les sérialiser et de les envoyer en réponse au format JSON. Pourquoi alors charger des entités? Il n'est pas nécessaire d'ajouter ces données à l'UOW de suivi des modifications, pour lire l'entité entière avec les champs "supplémentaires". Au lieu de cela, vous pouvez toujours écrire Select ou ProjectTo . Lazy Load n'est pas nécessaire car le code C # de Select sera traduit en SQL et exécuté côté base de données.

Que faire si ma logique ne se traduit pas en SQL?


Je recommande de garder l' évaluation des clients désactivée. Premièrement, vous pouvez "aider" et ajouter un support pour les fonctions nécessaires directement dans sub . Ce n'est pas une mauvaise option en ce qui concerne l'informatique simple, pas les règles commerciales. Option numéro deux: extraire l'interface de l'entité et l'implémenter à la fois dans l'entité et dans le DTO.

Par exemple, dans la base de données, il y a deux champs: «prix sans remise» et «prix avec remise». Si le champ "prix discount" est rempli, alors utilisez-le, sinon, utilisez le champ avec le prix habituel. Ajoutez une règle de plus. Lors de l'achat de 3 produits, vous ne payez que pour les 2 plus chers, tandis que les remises régulières sont également prises en compte.

La mise en œuvre peut être la suivante:

 public interface IHasProductPrice { decimal BasePrice { get; } decimal? SalePrice { get; } } public class Product: IHasProductPrice { // ... a lot of code public decimal BasePrice { get; protected set;} public decimal? SalePrice { get; protected set;} } public class ProductDto: IHasProductPrice { public decimal BasePrice { get; set;} public decimal? SalePrice { get; set;} } public static class ProductCalculator { public static void decimal Calculate(IEnumerable<IHasProductPrice> prices) } 

Dans le sous-système d'écriture, Lazy Load n'est pas si effrayant


Dans le sous-système d'écriture, au contraire, bien souvent, seul l'identifiant pour l'écriture n'est pas suffisant. Toutes sortes de vérifications vous font souvent lire l'entité entière, car le paradigme d'objet implique de combiner des données et des opérations sur celles-ci au sein de l'objet de classe et de son invariant. Si le projet utilise DDD, les opérations d'écriture / modification doivent être effectuées via la racine d'agrégation, et donc uniquement sur un objet et ses dépendances. Un grand nombre de requêtes ne peuvent se produire que lorsque vous travaillez avec des collections associées.

Collections associées dans les agrégats


S'il y a trop de données dans la machine, cela peut indiquer un problème de conception. Racines typiques d'agrégation - panier, commande, emballage. Les gens ne travaillent généralement pas avec des données provenant de milliers de lignes, donc le téléchargement de l'intégralité de la collection liée peut ne pas être l'opération la plus productive, mais pas mortelle. Mais s'il y a des milliers d'objets dans la collection, il est possible qu'il n'y ait vraiment pas de telle racine d'agrégation et les développeurs l'ont inventée, car c'était très simple de le faire en utilisant des outils improvisés.

Que se passe-t-il s'il reste des milliers d'enregistrements au total?


Passez le DbContext au constructeur et ne lisez que les données nécessaires dans le contexte de l'opération. Oui, violez DIP. Soit cela, soit n'utilisez pas du tout l'appareil dans ce cas.

Opérations de masse


L'importation d'un fichier de 10 000 lignes est une excellente cible pour Lazy Load. Ici, à tous les problèmes du sous-système de lecture, les freins de ChangeTracker sont également ajoutés. Pour l'enregistrement de masse, vous devez utiliser des outils distincts. Je préfère les extensions par lots, car là encore, vous pouvez vous passer de créer des entités. Pour les cas particulièrement graves, il existe de bonnes anciennes procédures stockées et même des outils SGBD spéciaux .

Life hack


Si vous devez mettre en œuvre à la fois une opération de masse et une opération conventionnelle, vous devez commencer par une opération de masse. Une opération normale n'est qu'un cas particulier de code de masse dans une séquence avec un seul élément.

Source: https://habr.com/ru/post/fr437418/


All Articles