web-dev-qa-db-fra.com

Confusion sur l'endroit où placer la logique métier lors de l'utilisation de la structure Entity

Je viens tout juste de commencer à travailler avec le framework Entity et je ne comprends pas comment les classes normalement de la couche de gestion s'intègrent aux entités créées par Entity Framework.

Lorsque je travaille avec ADO.NET classique, j'aurais une classe appelée Client par exemple, puis une autre classe appelée DALCustomer pour gérer l'interaction de base de données. Dans cette structure, j'aurais mis le code pour effectuer des calculs, filtrer et supprimer une instance du DAL avec Client pour la sauvegarde, la mise à jour et la suppression dans la classe Client.

Avec Entity Framework, si vous avez une table appelée Customer, le framework Entity crée une entité appelée Customer et c'est là que ma confusion commence: cette entité supprime-t-elle la nécessité d'un client dans la couche de gestion? Tous les champs et méthodes normalement utilisés dans la couche de gestion entrent donc dans l’entité générée par Entity Framework? Ou une classe doit-elle toujours exister dans la couche de gestion, par exemple CustomerBL, qui contient toujours les champs et les méthodes nécessaires pour accomplir la logique de gestion requise pour les calculs, le filtrage et nécessite toujours une instance de la DAL EF déclarée pour gérer l'accès aux données?

S'il doit y avoir une classe métier, dans ce cas-ci CustomerBL, une autre question s'impose: les champs créés dans l'entité client doivent-ils être recréés dans CustomerBL ou une instance de l'entité Client doit-elle être déclarée dans CustomerBL pour qu'il en soit ainsi? pas besoin d'avoir les champs déclarés dans 2 endroits?

48
obb-taurus

Le framework Entity, contrairement à linq-to-sql par exemple, a été conçu avec une séparation entre modèle de données et modèle conceptuel. Il prend en charge l'héritage , le fractionnement d'entités , le fractionnement de tables , les types complexes et les associations transparentes plusieurs à plusieurs , qui permettent toutes de mouler le modèle de domaine. à ses besoins sans être trop contraint par le modèle de magasin de données.

L'approche par le code d'abord permet de travailler avec des POCO dans lesquels les propriétés sélectionnées peuvent être mappées aux colonnes du magasin de données. Model-first et Database-first génèrent des classes partielles, ce qui permet d'étendre le code généré. Travailler avec ces classes a en grande partie l'apparence de travailler avec des POCO. Encore plus depuis la version 5, lorsque DbContext est devenue l'API par défaut, les classes générées ne sont plus gorgées de code lié à la persistance de l'API ObjectContext.

Bien entendu, cette séparation du modèle conceptuel et du modèle de magasin ne peut réussir que dans une certaine mesure. Certaines choses vont à l’encontre de cet objectif de persistance ignorance. Par exemple, si le chargement paresseux est souhaitable, il est nécessaire de déclarer les propriétés de navigation sous la forme virtual. EF peut donc les remplacer dans les types de proxy. Et il est très pratique d’avoir des propriétés de clé étrangère primitives (par exemple, ParentId) accompagnant les associations "réelles" (une référence Parent). Les puristes considèrent cela comme une violation de la conception pilotée par le domaine.

Une autre violation importante de l'ignorance de la persistance est le grand nombre de différences entre linq-to-objects et linq-to-entity . Vous ne pouvez simplement pas ignorer le fait que vous vous adressez à un univers totalement différent de celui des objets en mémoire. Ceci est appelé couplage étanche ou abstraction qui fuit .

Mais ensuite ... en général, je suis satisfait d’utiliser des classes EF ou des POCO générées à partir d’un modèle avec code en premier en tant que classes de domaine. Jusqu'à présent, je n'ai jamais vu une transition sans friction d'un magasin de données à un autre, si cela se produit du tout. La persistance, l'ignorance est une fiction. Les particularités du DAL laissent toujours une empreinte dans le domaine. Ce n'est que lorsque vous devez coder pour différents magasins/modèles de données ou que l'on s'attend à ce que les magasins/modèles changent relativement souvent qu'il soit rentable de minimiser autant que possible cette empreinte ou de l'éliminer complètement.

Un autre facteur à prendre en compte pour promouvoir les classes EF en tant que classes de domaine est que de nombreuses applications ont aujourd'hui plusieurs niveaux, où différents modèles de vue (en série) ou DTO (en série) sont envoyés à un client. L'utilisation de classes de domaine dans les interfaces utilisateur ne convient presque jamais. Vous pouvez aussi bien utiliser les classes EF en tant que domaine et faire en sorte que les services rejettent les modèles et les DTO dédiés à la demande d'une interface utilisateur ou de consommateurs de services. Une autre couche d'abstraction peut être plus un fardeau qu'une bénédiction, si ce n'est en termes de performances.

25
Gert Arnold

À mon avis, l’intérêt des POCO en tant qu’entités persistantes est de supprimer la distinction entre "entités de base de données" et "entités commerciales". Les "entités" sont censées être des "entités commerciales" qui peuvent directement être persistées et chargées à partir d'un magasin de données et donc agir en tant qu '"entités de base de données" simultanément. En utilisant des POCO, les entités commerciales sont découplées du mécanisme spécifique pour interagir avec une base de données.

Vous pouvez déplacer les entités dans un projet séparé, par exemple, ne contenant aucune référence à un assemblage EF et les utiliser dans un projet de couche de base de données pour gérer la persistance.

Cela ne signifie pas que vous pouvez concevoir complètement vos entités commerciales sans avoir à l'esprit les exigences relatives à EF. Vous devez connaître certaines limitations pour éviter tout problème lorsque vous arrivez au point de mapper les entités commerciales sur un schéma de base de données à l'aide de EF, par exemple:

  • Vous devez définir des propriétés de navigation (références ou collections de références à d'autres entités) virtual pour prendre en charge le chargement différé avec EF.
  • Vous ne pouvez pas utiliser IEnumerable<T> pour les collections qui doivent être persistées. Il doit s'agir de ICollection<T> ou d'un type plus dérivé.
  • Il n'est pas facile de conserver les propriétés private
  • Le type char n'est pas pris en charge par EF et vous ne pouvez pas l'utiliser si vous souhaitez conserver ses valeurs.
  • et plus...

Mais à mon avis, un ensemble supplémentaire d’entités est une couche supplémentaire de complexité qui devrait être justifiée pour être réellement nécessaire si les limitations mentionnées sont trop strictes pour votre projet.

YA2C (Encore 2 cents :))

13
Slauma

Je ne sais pas si c'est considéré comme une bonne pratique par d'autres mais personnellement, c'est comme cela que j'ai géré cela dans le passé:

Les classes générées par EF sont votre DAL, puis pour BL, créez un ensemble complémentaire de classes dans lesquelles vous disposerez de la structure requise (par exemple, la fusion de données d'entités liées dans une relation un à un) et d'autres problèmes de logique métier traités. (validation personnalisée comme implémenter IDataErrorInfo pour le faire jouer à Nice avec l'interface utilisateur dans WPF par exemple) et créer des classes qui contiendraient toutes les méthodes de la couche de gestion relatives à un type d'entité, utilisant les instances de BL et converties vers et à partir d'entités EF aux objets BL.

Ainsi, par exemple, vous avez Client dans votre base de données. EF générera une classe Customer et, dans le BL, il y aura une classe Customer (préfixe, suffixe, etc.) et une classe CustomerLogic. Dans la classe Client BL, vous pouvez faire tout ce qui est nécessaire pour satisfaire aux exigences sans devoir manipuler les entités EF. Dans la classe CustomerLogic, vous disposez des méthodes BL (chargez les clients les plus précieux, sauvegardez le client avec des données supplémentaires, etc.).

Maintenant, cela vous permet d'être couplé de manière lâche à l'implémentation de la source de données. Un autre exemple de pourquoi cela m’a été bénéfique dans le passé (dans un projet WPF) est qu’il est possible d’implémenter IDataErrorInfo et d’implémenter la logique de validation dans les classes CustomerBL de sorte que, lorsque vous liez l’entité à un formulaire de création/édition sur l'interface utilisateur vous bénéficierez de la fonctionnalité intégrée fournie par WPF. 

... Mes deux cents, je suis également curieux de savoir quelle est la meilleure pratique ou quelles sont les autres solutions/points de vue.


Peut-être aussi lié à ce sujet - Code-first vs Model/Database-first

5
dutzu

Ce sujet est peut-être un peu vieux mais cela peut aider. Andras Nemes a souligné dans son blog le souci d'utiliser DDD (conception pilotée par le domaine) par rapport à la conception pilotée par la technologie telle que EF, MVC, etc.

http://dotnetcodr.com/2013/09/12/a-model-net-web-service-based-on-domain-driven-design-part-1-introduction/

2
masterlopau

J'ai utilisé la logique métier pour écrire mes méthodes et renvoyer les résultats dans sa vue créée, comme:

namespace Template.BusinessLogic
{
    public interface IApplicantBusiness
    {
        List<Template.Model.ApplicantView> GetAllApplicants();

        void InsertApplicant(Template.Model.ApplicantView applicant);
    }
}
0
user3866304