Architecture de couche d'accès aux données (DAL) de hautes performances — Partie 2

DTO : Data Transfert Object (Objet de transfert de données)

Cette série de trois articles décrit comment écrire une couche d'accès aux données de hautes performances (DAL).

(Exemple de code en VB.NET)

Partie 2

Mise en œuvre de l'implémentation de la classe DALBase.

Étude de l'écriture des méthodes qui sont utilisées dans l'implémentation des classes PersonDb et autres classes EntityDb.

Commentez cet article : 1 commentaire Donner une note à l'article (5)

Article lu   fois.

Les deux auteur et traducteur

Traducteur : Profil ProSite personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

Voici un schéma de la conception DAL que nous utilisons.

Image non disponible

Dans la partie 1, nous avons parlé de l'architecture globale de la DAL et de l'utilisation de DTO pour déplacer des données dans et hors de la DAL. Nous avons également traité la classe PersonDb. Cette fois, nous allons regarder la classe DALBase et comment écrire le code pour construire les classes PersonDb et d'autres classes EntityDb.

II. La classe DALBase

Notre classe PersonDb contient l'ensemble de nos méthodes d'accès aux données pour obtenir et définir les données pour une entité personne. La dernière fois, nous écrivions des méthodes PersonDb qui ressemblaient à :

GetPersonByPersonGuid
Sélectionnez
' GetPersonByPersonGuid
Public Shared Function GetPersonByPersonGuid(ByVal PersonGuid As Guid) As PersonDTO
  Dim command As SqlCommand = GetDbSprocCommand("Person_GetByPersonGuid")
  command.Parameters.Add(CreateParameter("@PersonGuid", PersonGuid))
  Return GetSingleDTO(Of PersonDTO)(command)
End Function

Maintenant, nous devons écrire les méthodes de la classe DALBase nécessaires pour faire ce travail. Nous allons avoir besoin d'une méthode GetDbSprocCommand(). Certains CreateParameter() surchargés pour chaque type de paramètre que nous devons créer. Et, le plus important, nous aurons besoin de méthodes génériques GetSingleDTO()et GetDTOList() qui recevront un objet Command et qui transformeront (parse) ensuite les données en un seul DTO ou en une liste générique de DTO. Je vais passer en revue chaque partie de la classe DALBase, puis à la fin de ce document, je reprendrai le code complet de la classe.

III. Chaîne de connexion

Premièrement, nous devons gérer la chaîne de connexion. Nous allons créer une propriété pour encapsuler la chaîne de connexion du ConfigurationManager.

ConnectionString
Sélectionnez
' ConnectionString
Protected Shared ReadOnly Property ConnectionString() As String
  Get
    Return ConfigurationManager.ConnectionStrings("MyConnection").ConnectionString
  End Get
End Property

IV. SqlConnection et des objets de commande

Maintenant, nous allons créer des méthodes pour obtenir une instance de SQLConnection, une instance de SqlCommand pour le TSQL, et puisque nous allons utiliser des procédures stockées, une instance de SqlCommand pour une procédure stockée. Notez que GetDbSprocCommand() est la méthode que nous utilisons dans notre code PersonDb. Maintenant que nous l'écrivons, nous savons exactement les paramètres dont elle a besoin ainsi que son type de retour.

GetDbConnection
Sélectionnez
  ' GetDbConnection
  Protected Shared Function GetDbConnection() As SqlConnection
    Return New SqlConnection(ConnectionString)
  End Function' GetDbSqlCommand
  Protected Shared Function GetDbSQLCommand(ByVal sqlQuery As String) As SqlCommand
    Dim command As SqlCommand = New SqlCommand()
    command.Connection = GetDbConnection()
    command.CommandType = CommandType.Text
    command.CommandText = sqlQuery
    Return command
  End Function

  ' GetDbSprocCommand
  Protected Shared Function GetDbSprocCommand(ByVal sprocName As String) As SqlCommand
    Dim command As SqlCommand = New SqlCommand(sprocName)
    command.Connection = GetDbConnection()
    command.CommandType = CommandType.StoredProcedure
    Return command
  End Function

V. Création des SqlParameters

Maintenant que nous avons une méthode pour créer une instance de SQLCommand pour procédure stockée, nous devons être en mesure de créer des paramètres. Lorsque nous avons rédigé notre code de la classe PersonDb, nous avons fixé un objectif pour la façon dont nous voulons que le CreateParameter() fonctionne. Nous voulons une méthode CreateParameter() unique qui peut être utilisée pour ajouter des paramètres integer, guid, string, datetime, et nous voulons qu'elle ait une syntaxe simple qui fonctionne sur une seule ligne, comme indiqué ci-dessous.

 
Sélectionnez
command.Parameters.Add(CreateParameter("@Email", email, 100));

Cela devrait être possible. Notre méthode CreateParameter() prendra au moins deux paramètres. Le premier sera le nom du SqlParameter qui sera toujours une chaîne. Le deuxième sera la valeur du SqlParameter et le type de ce paramètre nous dit quel type de SqlParameter nous devrons créer. Donc, si nous avons une signature de méthode qui ressemble à CreateParameter(string name, guid value), nous savons que nous devons retourner un SqlParameter de Type SqlDbType.UniqueIdentifier. Si nous avons une signature de méthode qui ressemble à CreateParameter(string name, int value), nous savons que nous devons retourner un paramètre de type SqlDbType.Int. Voici à quoi nos méthodes CreateParameter() surchargées vont ressembler. Remarquez que nous avons ajouté un paramètre « size » supplémentaire à la signature de la surcharge qui crée un SqlParameter NvarChar.

CreateParameter
Sélectionnez
  ' CreateParameter - uniqueidentifier
  Protected Shared Function CreateParameter(ByVal name As String, ByVal value As Guid) As SqlParameter
    If value.Equals(DTOBase.Guid_NullValue) Then
      ' Si la valeur est null alors crée un paramètre null.
      Return CreateNullParameter(name, SqlDbType.UniqueIdentifier)
    Else
      Dim parameter As SqlParameter = New SqlParameter()
      parameter.SqlDbType = SqlDbType.UniqueIdentifier
      parameter.ParameterName = name
      parameter.Value = value
      parameter.Direction = ParameterDirection.Input
      Return parameter
    End If
  End Function

  ' CreateParameter - nvarchar
  Protected Shared Function CreateParameter(ByVal name As String, ByVal value As String, ByVal size As Integer) As SqlParameter
    If value = DTOBase.String_NullValue Then
      ' Si la valeur est null alors crée un paramètre null.
      Return CreateNullParameter(name, SqlDbType.NVarChar)
    Else
      Dim parameter As SqlParameter = New SqlParameter()
      parameter.SqlDbType = SqlDbType.NVarChar
      parameter.Size = size
      parameter.ParameterName = name
      parameter.Value = value
      parameter.Direction = ParameterDirection.Input
      Return parameter
    End If
  End Function

Donc, nous faisons une méthode comme celles ci-dessus pour chaque type de SqlParameter que nous devons créer (datetime, int, etc.). Nous avons également besoin d'avoir un moyen de créer des SqlParameters avec une valeur null, et des paramètres de sortie. La meilleure façon que j'aie trouvée pour le faire est d'utiliser les techniques ci-dessus pour réaliser une méthode CreateOutputParameter() supplémentaire et une méthode CreateNullParameter(). Le code correspondant peut être trouvé dans le listing complet de la classe à la fin de ce document.

VI. GetSingleDTO()

Nous arrivons à quelque chose d'un peu plus intéressant. Nous avons codé les méthodes prédéfinies nécessaires pour créer un objet de commande et ajouter les paramètres associés.

Maintenant, nous devons prendre cet objet de commande et obtenir de lui un ensemble utilisable de données, notre PersonDTO. C'est le but de GetSingleDTO() qui prend un objet de commande, ouvre sa connexion, appelle ExecuteReader() sur cette dernière, tente d'obtenir un seul DTO du reader (lecteur), puis le renvoie. Puisque nous ne voulons pas de réécriture de code pour chaque type de DTO, nous avons fait une méthode générique GetSingleDTO qui accepte n'importe quel type qui hérite de DTOBase. Vous remarquerez que GetSingleDTO() encapsule la logique qui serait autrement répétée dans chaque méthode d'accès aux données que nous écrivons. Des opérations comme l'ouverture de la connexion, l'obtention d'un reader ainsi que la vérification de la présence de lignes dans le reader sont des fonctionnalités que nous faisons de la même façon à chaque fois que nous avons besoin d'obtenir des données. Alors, pourquoi écrire encore et encore ? Nous pouvons simplement tout encapsuler ici dans notre méthode GetSingleDTO(). En fait, nous n'avons pas vraiment le choix. Nous avons tout encapsulé ici parce que nous avons déjà écrit notre code d'accès aux données PersonDb et il exige que GetSingleDTO() prenne un objet de commande comme paramètre et renvoie un DTO du type demandé. Nous avons codé notre méthode pour cette façon de procéder.

GetSingleDto
Sélectionnez
  ' GetSingleDTO
  Protected Shared Function GetSingleDTO(Of T As DTOBase)(ByRef command As SqlCommand) As T
    Dim dto As T = Nothing
    Try
      command.Connection.Open()
      Dim reader As SqlDataReader = command.ExecuteReader()
      If reader.HasRows Then
        reader.Read()
        Dim parser As DTOParser = DTOParserFactory.GetParser(GetType(T))
        parser.PopulateOrdinals(reader)
        dto = DirectCast(parser.PopulateDTO(reader), T)
        reader.Close()
      Else
        ' S'il n'y a pas de données, nous renvoyons null.
        dto = Nothing
      End If
    Catch e As Exception
      Throw New Exception("Error populating data", e)
    Finally
      command.Connection.Close()
      command.Connection.Dispose()
    End Try
    ' Renvoie le DTO, rempli soit avec des données soit avec null.
    Return dto
  End Function

Cette méthodologie prend place après que nous confirmions que le reader a vraiment des lignes (HasRow). À ce moment-là, nous appelons Read() et créons un DTOParser pour le type que nous voulons renvoyer. Les deux prochaines étapes sont la clé. D'abord, nous appelons parser.PopulateOrdinals(). Cette méthode reçoit et affecte les ordinaux pour chaque champ de données que nous devons extraire du Reader. Un ordinal est essentiellement un indice qui vous indique où trouver une valeur de champ de données spécifique dans le reader. Obtenir des valeurs par ordinal (index) est beaucoup plus efficace que d'obtenir des valeurs par le nom de champ (colonne) de données. Après avoir renseigné les ordinaux, nous sommes alors en mesure d'utiliser le Parser (analyseur/répartiteur) pour remplir l'objet DTO que notre méthode renverra.

Vous remarquerez que le code ci-dessus ne contient aucune logique pour parser le DTO en cours depuis le reader. C'est parce que les changements logiques dépendent du type de DTO que nous recevons. Donc, nous encapsulons ce qui change dans un objet DTOParser. Nous allons alors simplement utiliser le DTOParser pour obtenir le type de DTO que nous recherchons et nous laissons le DTOParser se soucier de la façon dont le parsing (analyse/répartition) est réellement fait.

La méthode GetDTOList() utilise le même design que ci-dessus, elle retourne simplement une liste générique de DTO lieu d'un seul DTO. La partie de code de GetDTOList() est incluse dans la classe complète située à la fin de ce document.

VII. Résumé

Maintenant, nous avons couvert la classe DALBase. Dans le prochain article, nous allons entrer dans la vraie magie de la performance où nous prendrons ce reader, lequel sera passé au DTOParser, et utilisera les données ordinales ainsi que les méthodes (Get) fortement typées pour extraire des données de la manière la plus efficace possible sans caster l'objet.

Voici le listing complet de DALBase.

La classe DALBase
Sélectionnez
Public MustInherit Class DALBase
  ' ConnectionString
  Protected Shared ReadOnly Property ConnectionString() As String
    Get
      Return ConfigurationManager.ConnectionStrings("MyConn").ConnectionString
    End Get
  End Property

  ' GetDbSqlCommand
  Protected Shared Function GetDbSQLCommand(ByVal sqlQuery As String) As SqlCommand
    Dim command As New SqlCommand()
    command.Connection = GetDbConnection()
    command.CommandType = CommandType.Text
    command.CommandText = sqlQuery
    Return command
  End Function

  ' GetDbConnection
  Protected Shared Function GetDbConnection() As SqlConnection
    Return New SqlConnection(ConnectionString)
  End Function

  ' GetDbSprocCommand
  Protected Shared Function GetDbSprocCommand(ByVal sprocName As String) As SqlCommand
    Dim command As SqlCommand = New SqlCommand(sprocName)
    command.Connection = GetDbConnection()
    command.CommandType = CommandType.StoredProcedure
    Return command
  End Function

  ' CreateNullParameter
  Protected Shared Function CreateNullParameter(ByVal name As String, ByVal paramType As SqlDbType) As SqlParameter
    Dim parameter As SqlParameter = New SqlParameter()
    parameter.SqlDbType = paramType
    parameter.ParameterName = name
    parameter.Value = Nothing
    parameter.Direction = ParameterDirection.Input
    Return parameter
  End Function

  ' CreateNullParameter - avec la taille pour nvarchars
  Protected Shared Function CreateNullParameter(ByVal name As String, ByVal paramType As SqlDbType, ByVal size As Integer) As SqlParameter
    Dim parameter As SqlParameter = New SqlParameter()
    parameter.SqlDbType = paramType
    parameter.ParameterName = name
    parameter.Size = size
    parameter.Value = Nothing
    parameter.Direction = ParameterDirection.Input
    Return parameter
  End Function

  ' CreateOutputParameter
  Protected Shared Function CreateOutputParameter(ByVal name As String, ByVal paramType As SqlDbType) As SqlParameter
    Dim parameter As SqlParameter = New SqlParameter()
    parameter.SqlDbType = paramType
    parameter.ParameterName = name
    parameter.Direction = ParameterDirection.Output
    Return parameter
  End Function

  ' CreateOuputParameter - avec la taille pour nvarchars
  Protected Shared Function CreateOutputParameter(ByVal name As String, ByVal paramType As SqlDbType, ByVal size As Integer) As SqlParameter
    Dim parameter As SqlParameter = New SqlParameter()
    parameter.SqlDbType = paramType
    parameter.Size = size
    parameter.ParameterName = name
    parameter.Direction = ParameterDirection.Output
    Return parameter
  End Function

  ' CreateParameter - uniqueidentifier
  Protected Shared Function CreateParameter(ByVal name As String, ByVal value As Guid) As SqlParameter
    If value.Equals(DTOBase.Guid_NullValue) Then
      ' Si la valeur est null alors crée un paramètre null.
      Return CreateNullParameter(name, SqlDbType.UniqueIdentifier)
    Else
      Dim parameter As SqlParameter = New SqlParameter()
      parameter.SqlDbType = SqlDbType.UniqueIdentifier
      parameter.ParameterName = name
      parameter.Value = value
      parameter.Direction = ParameterDirection.Input
      Return parameter
    End If
  End Function

  ' CreateParameter - int
  Protected Shared Function CreateParameter(ByVal name As String, ByVal value As Integer) As SqlParameter
    If value = DTOBase.Int_NullValue Then
      ' Si la valeur est null alors crée un paramètre null.
      Return CreateNullParameter(name, SqlDbType.Int)
    Else
      Dim parameter As SqlParameter = New SqlParameter()
      parameter.SqlDbType = SqlDbType.Int
      parameter.ParameterName = name
      parameter.Value = value
      parameter.Direction = ParameterDirection.Input
      Return parameter
    End If
  End Function

  ' CreateParameter - datetime
  Protected Shared Function CreateParameter(ByVal name As String, ByVal value As DateTime) As SqlParameter
    If value = DTOBase.DateTime_NullValue Then
      ' Si la valeur est null alors crée un paramètre null.
      Return CreateNullParameter(name, SqlDbType.DateTime)
    Else
      Dim parameter As SqlParameter = New SqlParameter()
      parameter.SqlDbType = SqlDbType.DateTime
      parameter.ParameterName = name
      parameter.Value = value
      parameter.Direction = ParameterDirection.Input
      Return parameter
    End If
  End Function

  ' CreateParameter - nvarchar
  Protected Shared Function CreateParameter(ByVal name As String, ByVal value As String, ByVal size As Integer) As SqlParameter
    If value = DTOBase.String_NullValue Then
      ' Si la valeur est null alors crée un paramètre null.
      Return CreateNullParameter(name, SqlDbType.NVarChar)
    Else
      Dim parameter As SqlParameter = New SqlParameter()
      parameter.SqlDbType = SqlDbType.NVarChar
      parameter.Size = size
      parameter.ParameterName = name
      parameter.Value = value
      parameter.Direction = ParameterDirection.Input
      Return parameter
    End If
  End Function

  ' GetSingleDTO
  Protected Shared Function GetSingleDTO(Of T As DTOBase)(ByRef command As SqlCommand) As T
    Dim dto As T = Nothing
    Try
      command.Connection.Open()
      Dim reader As SqlDataReader = command.ExecuteReader()
      If reader.HasRows Then
        reader.Read()
        Dim parser As DTOParser = DTOParserFactory.GetParser(GetType(T))
        parser.PopulateOrdinals(reader)
        dto = DirectCast(parser.PopulateDTO(reader), T)
        reader.Close()
      Else
        ' S'il n'y a pas de données, nous renvoyons null.
        dto = Nothing
      End If
    Catch e As Exception
      Throw New Exception("Error populating data", e)
    Finally
      command.Connection.Close()
      command.Connection.Dispose()
    End Try
    ' Renvoie le DTO, rempli soit avec des données soit avec null.
    Return dto
  End Function

  ' GetDTOList
  Protected Shared Function GetDTOList(Of T As DTOBase)(ByRef command As SqlCommand) As List(Of T)
    Dim dtoList As New List(Of T)()
    Try
      command.Connection.Open()
      Dim reader As SqlDataReader = command.ExecuteReader()
      If reader.HasRows Then
        ' Obtenir un analyseur (parser) pour ce type de DTO et remplir les ordinaux.
        Dim parser As DTOParser = DTOParserFactory.GetParser(GetType(T))
        parser.PopulateOrdinals(reader)
        ' Utilise l'analyseur (parser) pour construire notre liste de DTO.
        While reader.Read()
          Dim dto As T = Nothing
          dto = DirectCast(parser.PopulateDTO(reader), T)
          dtoList.Add(dto)
        End While
        reader.Close()
      Else
        ' S'il n'y a pas de données, nous renvoyons null.
        dtoList = Nothing
      End If
    Catch e As Exception
      Throw New Exception("Error populating data", e)
    Finally
      command.Connection.Close()
      command.Connection.Dispose()
    End Try
    Return dtoList
  End Function
End Class

VIII. Remerciements

Je remercie M. Lacovara de m'avoir permis de traduire sa série d'articles « High Performance Data Access Layer Architecture ».

Gaëtan Wauthy et Kropernic pour la relecture et la validation technique, ainsi qu'une première relecture orthographique.

Claude Leloup pour la relecture orthographique.

IX. Source

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2013 Hervé Taraveau. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.