IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Architecture de couche d'accès aux données (DAL) de hautes performances - Partie 1
Version VB.NET du tutoriel traduit par Hervé Taraveau

Le , par rv26t

0PARTAGES

2  0 
Bonjour,

Cette discussion est destinée à recueillir vos commentaires sur l'article « Architecture de couche d'accès aux données (DAL) de hautes performances — Partie 1 » et des Data Transfer Object (DTO).
Traduction de l'article « High Performance Data Access Layer Architecture Part 1 » de M. Rudy Lacovara.

DTO, DAL, BLL… Tout le monde s'en sort ? Pas si sûr !
Voici le premier chapitre d'un article qui vous aidera à comprendre cette architecture et ses concepts-clés.
Dans cette première partie, nous allons nous intéresser à l'architecture globale de la DAL et l'utilisation des DTO pour transférer des données entre les différentes couches de l'application.
Nous allons aussi voir la mise en pratique de ces concepts à l'aide d'une classe PersonDB qui contiendra l'ensemble de nos méthodes d'accès aux données permettant d'obtenir et de sauvegarder les données d'une entité « personne ».
Cette série de trois articles décrit comment écrire une couche d'accès aux données de hautes performances.
Lien sur les discussions des autres articles : Discussion sur la partie 2, Discussion sur la partie 3.

Bonne lecture.

Une erreur dans cette actualité ? Signalez-nous-la !

Avatar de meziantou
Membre émérite https://www.developpez.com
Le 30/12/2013 à 19:06
D’un côté, certains préfèrent tout écrire à la main en utilisant directement ADO.NET sans surcouche. D’un autre côté les fervents défenseurs des ORM. En gros d’un côté on aime faire de la plomberie alors que de l’autre non . Personnellement je n'aime pas la plomberie mais je n'utilise pas pour autant un ORM.

Je tenais juste à présenter un produit avec une approche différente : CodeFluent Entities. En gros comme Entity Framework, on part d’un modèle représentant des entités, des énumérations, des propriétés, des méthodes, des règles, des vues, etc. (on peut modéliser un peu plus de concepts qu’EF (le document date de 2012 mais beaucoup de choses restent vraies)). On ajoute ensuite des producteurs dont le but est de générer le code (SQL Server, Oracle, PostgreSQL, C#, VB.NET, WCF, Proxy WCF, ASP.NET, etc.). Tout le code généré est statique (Procédures stockés, code d’accès aux données…) et pour l'accès aux données, seuls des procédures stockées et ADO.NET sont utilisés, ce qui devrait plaire à rv26t .
A vrai dire il y a une très légère surcouche nommée CodeFluentPersistence dont le but est de masquer les différences entre les différents SGBD, notamment au niveau des paramètres (par exemple certains utilisent @param, d'autres :param) ou des erreurs (duplicate, concurrence). Bref ça reste très très léger.

On a donc les avantages d’une approche Model-First tout en ayant au final un code totalement statique et facilement lisible (donc debuggable au besoin). Pour ceux qui se demandent quels sont les avantages du model first.

Pour ceux qui veulent refaire un semblant de CodeFluent Entities avec EF (oui on en est encore assez loin), vous pouvez regarder le blog de Matthieu Mezil et son WAQS (c’est un fan absolu d'EF et des T4).

Il ne faut pas non plus oublier un problème assez important vis à vis d'Entity Framework : le travail en équipe est un véritable calvaire !
CodeFluent Entities gèrent cela sans trop de problème. Le modèle peut être découpé en autant de fichiers que nécessaire (un par entité, un par namespace, ou autre), alors qu'avec EF on a un fichier EDMX pour tout le modèle. Au niveau du designer on peut spécifier quels éléments doivent être affichés sur chaque surfaces (contrairement à EF on peut avoir plusieurs surfaces). Cela permet également de travailler avec de gros modèle.
4  0 
Avatar de Kropernic
Expert confirmé https://www.developpez.com
Le 24/12/2013 à 9:55
Citation Envoyé par Immobilis Voir le message
Salut,Ce type de clef est plutôt une contrainte d'intégrité qu'une vrai clef (en général dangereusement basée sur un concept fonctionnel). Mais bon ce n'est pas le sujet du topic.
Ce type de clef découle d'une modélisation respectant les règles de gestion. Je suis d'accord avec toi, ce n'est pas l'objet du topic. Néanmoins, si CodeFirst ne le permet pas, c'est pour moi déjà un point éliminatoire.
Citation Envoyé par Immobilis Voir le message
Oui. Mais ton implémentation de DAL ne fait pas tout ça non plus.
Ce n'est pas le rôle d'un ORM. Un ORM ne remplace pas le travail d'un administrateur de base de données. Tu te trompes sur l'usage.

Tu sembles réfractaire... Cela m'arrive aussi. Je te suggère d'essayer

A+
Non, ma DAL ne fait pas cela en effet. Mais elle ne crée pas la DB non plus.

Avec Code First, c'est en fait la DB qui s'adapte au code. Or je crois profondément que cela de devrait jamais être le cas. Pour tout projet impliquant de stocker de l'information, je pense que la première chose à faire est de mettre à plat toute l'information de l'univers concerné par le projet et de voir/comprendre comme elle s'organise. Une fois la DB correctement normalisée et après vérification de toutes règles de gestion par la MOA, alors seulement, il est temps de penser à la partie software du projet.

Les bases d'un projet d'information de gestion SONT les données (et par conséquent, la DB). Une DB fortement normalisée et avec les contraintes appropriées permettra la détection automatique d'une tonne de "bugs" en phase de dev/test. Il est si facile d'oublier de faire une vérification avant d'insérer une ligne dans une table qui n'aurait pas y être.

Bref, je vais m'arrêter là, je crois qu'on a compris que je place les données avant tout dans un projet en impliquant.

Je suis purement programmeur de formation mais dans la boîte où je suis, on s'occupe de tout du début à la fin. Ce qui m'a fait prendre conscience de l'importance incommensurable d'une base de données correctement modélisée/normalisée. Une fois cela établi, le développement de l'application est grandement facilité. Il suffit de suivre les rails posées par la DB.
3  0 
Avatar de Immobilis
Expert éminent https://www.developpez.com
Le 19/12/2013 à 9:47
Citation Envoyé par Kropernic Voir le message
Lourdeur de maintenance ???
Ben oui, L'ajout d'un champ implique une intervention dans le code que je trouve très lourde et génératrice de bug. Mais bon comme tout c'est un choix.
Citation Envoyé par Kropernic Voir le message
J'aime avoir le contrôle "absolu" sur la moindre chose que je fais.
C'est une illusion dans la mesure où tu utilises déjà un Framework qui fait 99,99% du boulot. A moins de coder en langage machine tu ne contrôles pas tout.
Citation Envoyé par Kropernic Voir le message
Je n'ai encore jamais utilisé d'ORM
Ceci explique peut-être cela

A+
2  0 
Avatar de Immobilis
Expert éminent https://www.developpez.com
Le 19/12/2013 à 12:18
Citation Envoyé par Kropernic Voir le message
Tout ce qu'il y a à faire, c'est d'ajouter la propriété dans le DTO et de gérer son assignation dans le parser...
Je comprend bien sauf que dans cette phrase qui a l'air simple tu réalises des opérations lourdes (à mon avis): ajout de propriétés en cascade, des noms de colonnes en dur, des conditions, des initialisations. En fait ça me rappelle des méthodes que j'utilisais en 2006...
Sinon, tu as oublié la mise à jour du constructeur du DTO (alors que le Framework instancie lui-même les types "valeur", de la méthode PersonDb.SavePerson pour lequel il faut aussi mettre à jour le paramètre. Tu vois, deux oublis, pas si simple.
Une autre raison pour laquelle je n'utilise plus ce système est que je ne vois plus où est l'avantage (le gain) à faire des méthodes du type GetMonObjectByXXXXXX(string). C'est bien plus pratique/fiable d'utiliser une expression lambda sur un repository qui va adapter la requête SQL.
De plus, le gain de temps de l'ordre de la milliseconde est négligeable par rapport au reste de l'exécution de la page. En proportion ok c'est deux fois plus peut-être mais cela reste insignifiant si tu ne fais pas de l'aéronautique.
Citation Envoyé par Kropernic Voir le message
Pareil ici. J'utilise les briques du framework et je ne fais pas appel à un entrepreneur (ORM).
Entity Framework fait parti du Framework .NET. Pour moi tu moules tes parpaings alors qu'on en trouve de très bons sur le marché. Le seul entrepreneur auquel tu fais appel c'est ADO .NET auquel tu délègues la récupération de tes données auprès de la base (le sable, l'eau, le ciment). L'assemblage, la création de l'objet, c'est toi qui le fais.
Citation Envoyé par Kropernic Voir le message
avant de revenir à mes requêtes/procédures stockées écrites moi-même.
L'utilisation de procédures stockées et d'un ORM est effectivement la meilleure solution.
A+
2  0 
Avatar de rv26t
Modérateur https://www.developpez.com
Le 19/12/2013 à 10:21
Citation Envoyé par Kropernic Voir le message
... mise à part la factory où, selon l'exemple de l'article, les noms des objets sont hard codés, ...
Justement, j'ai fait une petite modif de ce coté. J'essayerais de faire un billet.
J'ai rendu la méthode générique (note : je ne parle pas des paramètres de la requête pour GetSingleDTO que je passe de façon particulière, ce n'est pas le but du sujet)
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
''' <summary>DTOParserFactory : fabrique pour obtenir un objet analyseur (parser).</summary>
''' <remarks>Les méthodes sont déclarées en Shared.</remarks>
Public NotInheritable Class DTOParserFactory

    ''' <summary>Fabrique générique d'analyseur, (Parser) Cré une instance de classe concrête quelconque de type « DTOParser ».</summary>
    ''' <typeparam name="T">Un type de classe héritant de DTOParser abstraite.</typeparam>
    ''' <returns>Une instance de la classe DTOParser spécifique demandé par l'application utilisatrice.</returns>
    ''' <remarks>Méthode générique. Performance. Déclarée en Shared.</remarks>
    Public Shared Function FabriqueClasse(Of T As {New})() As T
        Return New T
    End Function
Et dans ma nouvelle méthode GetSingleDTO qui se présente ainsi
Code : Sélectionner tout
1
2
3
4
5
6
7
8
    ''' <summary>Lit une ligne de données renvoyé sous forme d'une instance de classe définie par l'appelant.</summary>
    ''' <typeparam name="T">Type de classe analyseur qui sera utilisé pour créer un DTO léger recevant les données.</typeparam>
    ''' <param name="Requete">La requête de sélection.</param>
    ''' <returns>Une instance de classe DTO léger d'un ensemble ligne de données.</returns>
    ''' <remarks>Méthode générique ne connaissant pas le type de classe analyseur en entrée. 
    ''' Une fabrique générique renvoie une instance de cette classe analyseur,
    ''' une méthode de cette instance retourne une instance DTO léger contenant l'ensemble d'une ligne de données.</remarks>
    Public Function GetSingleDTO(Of T As {New})(ByVal Requete As String) As DTOBase
Je fais un appel à la fabrique de cette façon (dans GetSingleDTO)
Code : Sélectionner tout
parser = DTOParserFactory.FabriqueClasse(Of T)()
Pour un ensemble d'info (de DTO) j'ai donc une méthode qui renvoi une liste
Code : Sélectionner tout
1
2
    ''' <summary>Lit plusieurs lignes de données renvoyé sous forme d'une liste d'instance de classe définie par l'appelant.</summary>
    Public Function GetListDTO(Of T As {New})(ByVal Requete As String) As List(Of DTOBase)
Cela fonctionne très bien, mais il me reste un petit souci avec le parser que je dois déclarer de type object, je préférerais arriver à le typer.

Voilà, simplement pour dire que l'on peut, comme l'indique Kropernic, puiser des idées et adapter à ses besoins.
Les messages arrivent plus vite que mes réponses.
1  0 
Avatar de plume13
Membre confirmé https://www.developpez.com
Le 24/12/2013 à 17:45
Bonjour à tous, j'ai lu avec intérêt vous posts et les tutos cités. Je ne connaissais pas CodeFirst et c'est vrai que cela a l'air simple et pratique. Les développeurs n'ont plus besoin de s'y connaître en BDD. Le rêve quoi !
Sauf que j'ai du mal à croire que ce soit aussi magique... Je suis plutôt de l'avis de Kropernic sur le fait que le coeur d'un logiciel de gestion, c'est la base de données, et qu'il est préférable de garder la main dessus et que peu de personnes puissent modifier son schéma.
Cependant, j'utilise EF en Database-First avec modèle T4 d'objets POCO et j'en suis satisfaite : je garde la main tout en m'évitant l'écriture de code d'ajout/modification/etc. Par contre, je fais bien attention à ce que j'écris en linq, histoire de pas me retrouver avec des requêtes Sql de 300 lignes.
Cela dit, j'essaierai un jour pour voir ce que ça donne vraiment, je pourrais être surprise
1  0 
Avatar de Immobilis
Expert éminent https://www.developpez.com
Le 25/12/2013 à 12:11
Citation Envoyé par Kropernic Voir le message
Ce type de clef découle d'une modélisation respectant les règles de gestion.
C'est justement ce qu'il faut éviter. Les règles de gestion métier sont changeantes, incomplètes, dépendantes des méthodes de travail. Cela fait porter un risque sur l'intégrité de la base de données. A mon avis, les clefs ne devraient être que des identifiants uniques auto-générés (guid, entier). L'intégrité des données peut être garanties par d'autres moyens triggers, contraintes, ...

Citation Envoyé par Kropernic Voir le message
Avec Code First, c'est en fait la DB qui s'adapte au code.
Je ne dirai pas ça comme ça mais plutôt le modèle (le terme est important) est créé via une IHM de design dans Visual Studio. Le produit obtenu est un script SQL.

Sinon, les approches modèle et code first sont similaires. Dans le premier cas, je dirai qu'on se laisse guider par un assistant et comme en général le model est souvent très proche de la structure de la base (ou inversement), c'est une excellente entrée en matière.

Une fois la DB correctement normalisée et après vérification de toutes règles de gestion par la MOA
Si le modèle se base sur des métadonnées cela devient difficile à montrer. Dans les cas "normaux", cela se fait aussi très bien avec EF et Visual Studio.

Citation Envoyé par plume13 Voir le message
j'utilise EF en Database-First avec modèle T4 d'objets POCO et j'en suis satisfaite
Faudrait que j'essaye ça aussi.

Quelque soit l'approche c'est surtout une question d'affinité avec les outils de développement. Il n'y a pas UNE façon de développer.

A+
1  0 
Avatar de rv26t
Modérateur https://www.developpez.com
Le 27/12/2013 à 23:30
@Kropernic
Citation Envoyé par Kropernic Voir le message
mise à part la factory où, selon l'exemple de l'article, les noms des objets sont hard codés...
Citation Envoyé par rv26t Voir le message
Justement, j'ai fait une petite modif de ce coté. J'essayerais de faire un billet.
J'ai rendu la méthode générique (note : je ne parle pas des paramètres de la requête pour GetSingleDTO que je passe de façon particulière, ce n'est pas le but du sujet)
Voici la méthode GetListDTO avec les classes de travail générique en paramètres (T pour le type de classe parser, V pour le type de classe DTO qui constituera la liste) pour répondre à toutes construction de liste de DTO de façon générique. (mais avec des éléments typés) (GetSingleDTO idem)
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    ''' <summary>Lit plusieurs lignes de données renvoyé sous forme d'une liste d'instance de classe DTO définie par l'appelant.</summary>
    ''' <typeparam name="T">Type de classe analyseur qui sera utilisé pour créer un DTO léger recevant les données.</typeparam>
    ''' <typeparam name="V">Type de classe DTO léger recevant les données.</typeparam>
    ''' <param name="Requete">La requête de sélection.</param>
    ''' <returns>Une liste d'instances de classe DTO pour l'ensemble de ligne de données.</returns>
    ''' <remarks>Méthode générique ne connaissant pas le type de classe analyseur en entrée, ni le type de DTO.
    ''' Une fabrique générique renvoie une instance de cette classe analyseur, une méthode de cette instance (PopulateDTO) retourne une instance DTO léger contenant l'ensemble d'une ligne de données.
    ''' Une boucle permet la lecture de toutes les lignes et d'alimenter la liste retournée. Une liste de DTO correspondant au type (V) (passé en paramètre) est ainsi constituée, et renvoyée.</remarks>
    Public Function GetListDTO(Of T As {New}, V)(ByVal Requete As String) As List(Of V)
        Dim ListeDTO As List(Of V) = New List(Of V)
                    ' suite code ...
                    ' ...
                    parser = DTOParserFactory.FabriqueClasse(Of T)()
                    ' suite code ...
                    ' ...
                    Return ListeDTO
Le paramètre peut rester (ByRef command As SqlCommand) comme dans le tuto au lieu de la requete
1  0 
Avatar de rv26t
Modérateur https://www.developpez.com
Le 13/01/2014 à 14:42
@meziantou
Merci pour ces infos (désolé pour le temps de réponse)
Connaître d'autres solutions telle que celle que tu présentes est très intéressant.
Cela permet de choisir la meilleure approche suivant le type de projet.

A ce titre il me semble judicieux d'indiquer aussi l'existance de Dapper qui est vraiment très léger et permet une approche très simple.
1  0 
Avatar de Immobilis
Expert éminent https://www.developpez.com
Le 19/12/2013 à 1:02
Salut,

Intéressant mais les références citées sont un peu anciennes (2008 et 2009). Outre l'architecture tu aurais pu refaire des tests avec les derniers Framework, non?

Personnellement, compte tenu de la lourdeur de la maintenance d'une telle architecture, en dehors d'une course à la microseconde, il y a peu de chance que je revienne à cette façon de faire.

A+
0  0