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

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

VB.NET Discussion :

POCO dans la couche DAL vs DTO


Sujet :

VB.NET

  1. #21
    Modérateur

    Homme Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 722
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 722
    Points : 5 100
    Points
    5 100
    Par défaut
    Moi aussi cela m'oblige à me demander si j'ai bien compris.

    Citation Envoyé par Sankasssss Voir le message
    Il parle bien d'un héritage un à un, donc un DTO à toujours son équivalent POCO qui l'encapsule.

    Donc pour moi le POCO est bien un objet servant à contenir la logique métier du DTO qui ne peut pas en avoir.
    Je suis bien d'accord avec toi lorsque l'on manipule un DTO, (le DTO n'est qu'un conteneur de données)

    D'ailleurs dans son article, il prend bien l'exemple de l'utilisation d'un DTO dans un POCO pour lire les données d'une personne et la modifier. (ce qui est logique vu le type de l'exemple)
    Il indique juste au dessus du code.
    je n'ai qu'une seule propriété nommée Data qui est de type PersonDTO. Cette approche permet de lire et modifier une personne très facilement. Lors de la lecture d'une personne, je récupère seulement un PersonDTO de ma DAL et j'affecte la propriété person.Data = personDTO. Lors de la sauvegarde, mes méthodes prennent toutes un PersonDTO comme paramètre de telle sorte que je peux utiliser ma propriété person.Data pour cela.
    représenté par le code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
            // Person - prend un DTO
            public Person(PersonDTO dto) {this.Data = dto;}
    Parcequ'il prend l'exemple de la manipulation des données d'une personne.
    C'est dommage qu'il ne donne pas d'exemple avec la manipulation des données d'un ensemble de personnes. (Cela sens le terrain piégé sur lequel il ne faut pas trop s'aventurer)
    _____

    Donc, je ne voulais pas parler du cas ou l'on récupère un DTO, (je dois mal m'exprimer)
    mais du cas ou l'on récupère une collection de DTO avec
    Tu vas parcourir la collection de DTO pour créer une nouvelle collection de POCO contenant chacun un seul DTO ? (cela me surprend)
    Personnellement, j'aurais fait un seul POCO qui contient une propriété Collection de DTO et des méthodes qui manipulent cette collection de DTO suivant mes besoins. Parceque les besoins ne sont pas les mêmes lorsque l'on manipule un élément ou une collection d'éléments.

    Mais bon, reconstruire une collection de POCO avec chacun un seul DTO est peut-être une autre approche. (Ce qui me semble bizarre c'est que dans ce cas, imaginons 5 éléments dans la collection, tu as donc 5 instances de POCO qui contiennent chacune les mêmes méthodes mais 5 DTO différents, quelque chose me géne)
    Vu que je n'utilisais pas les POCO, je loupe peut-être quelque chose, il faut que je réflèchisse. Je vais regarder ton lien.

    Citation Envoyé par Sankasssss Voir le message
    C'est vraiment dommage qu'il n'y ai pas plus d'exemple sur le net (ou qu'il soit si dur d'en trouver). en plus les définitions que l'on trouve sont elle aussi très basique et n'en disent pas beaucoup plus.
    Tout a fait.
    Et que M. Lacovara ne donne pas plus d'exemple.
    Mais il me semble que l'ensemble POCO + DTO va correspondre a des gestions relativement simples, mais que lorsque l'on va commencer a avoir des gestions complexe dans la BLL, cela va devenir un casse-tête.
    Traductions d'articles :
    La mémoire en .NET - Qu'est-ce qui va où ?
    Architecture DAL de haute performance et DTO ; Version C# : Partie 1,Partie 2,Partie 3 — Version VB.NET : Partie 1,Partie 2,Partie 3
    N'hésitez pas à consulter la FAQ VB.NET, le cours complet de Philippe Lasserre et tous les cours, articles et tutoriels.

  2. #22
    Modérateur
    Avatar de Sankasssss
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Novembre 2006
    Messages
    1 842
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 842
    Points : 4 232
    Points
    4 232
    Par défaut
    Citation Envoyé par rv26t Voir le message
    Parcequ'il prend l'exemple de la manipulation des données d'une personne.
    C'est dommage qu'il ne donne pas d'exemple avec la manipulation des données d'un ensemble de personnes. (Cela sens le terrain piégé sur lequel il ne faut pas trop s'aventurer)
    On y est !!!

    Citation Envoyé par rv26t Voir le message
    Tu vas parcourir la collection de DTO pour créer une nouvelle collection de POCO contenant chacun un seul DTO ? (cela me surprend)
    Oui effectivement, c'est ce que je trouvais énormément lourd :
    Citation Envoyé par Sankasssss Voir le message
    Je devrais faire un objet PersonRepository qui me retournerait des POCO person mais donc aurait les mêmes méthodes que ma couche DAL comme GetColPersonsByAge mais ces dernières méthodes ne serait qu'une surcharge qui bouclerait sur la collection de DTO pour en faire une collection de POCO. Du coup je me retrouve avec deux énumérations de collection juste pour séparer l'accès au donnée de la logique métier.
    D'où ma question sur une éventuelle fusion des DTO et POCO.

    Concernant :
    Citation Envoyé par rv26t Voir le message
    Mais bon, reconstruire une collection de POCO avec chacun un seul DTO est peut-être une autre approche. (Ce qui me semble bizarre c'est que dans ce cas, imaginons 5 éléments dans la collection, tu as donc 5 instances de POCO qui contiennent chacune les mêmes méthodes mais 5 DTO différents, quelque chose me géne)
    C'est la méthode que j'utilisais en partie sur mon ancien modèle, sauf que je ne passais par des DTO. Mon équivalent DAL, qui encapsulait une dataTable, pouvait me retourner un équivalent POCO.
    Malheureusement mon ancien modèle avait un manque car à chaque nouvelle énumération d'un élément, je reconstruisais un nouveau "POCO" à partir d'une dataRow et je perdais toutes les variables de traitement qui pourraient être très utile à garder.

    Concernant la duplication des méthodes, je ne pense pas que cela soit un problème. Elles contiennent bien 5 fois les mêmes méthodes mais qui n'agissent pas sur les mêmes données. C'est la même chose quand on fait une List(Of String), tous les objets ont bien toutes les méthodes de la class String.


    Concernant mon ancien modèle, j'ai réellement remarqué les problèmes de performances sur ma dernière application où une mise en forme de données sous forme de tableau croisé de deux table mettait plus d'une vingtaine de seconde car j'avais une boucle qui appelait des filtres sur les DataTables (datatable.select) et c'est là que j'ai vu des discussions concernant les DTO/POCO.

    Vu que j'encapsulais ma table dans un nouvelle objet que je nommais classDonneeNOMTABLE, je mettais toutes les méthodes qui devaient s'appliquer sur un ensemble de données dans cette class vu que c'est elle qui encapsulait la DataTable.
    Malheureusement, elle gérait aussi tout les systèmes de chargement et de sauvegarde.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
     
    Public Class ClassDonneeAUTAPP
        Inherits ClassDonnee
    #If DEBUG Then
        Public Const strBibliothequeEtFichier As String = "PERSO.AUTAPP"
    #Else
    		Public Const strBibliothequeEtFichier As String = "PROD.AUTAPP"
    #End If
     
    #Region "Code créé par le programme G00039VB"
        Public Sub New(ByRef connection As OleDb.OleDbConnection)
            MyBase.New(connection)
        End Sub
        Public Overrides Sub chargeStructuretable()
            If table Is Nothing Then
                table = New DataTable
            Else
                table.Clear()
                Exit Sub
            End If
     
            Try
                Dim strSql As String
                strSql = "Select * from " & strBibliothequeEtFichier & " where ID = 1"
                Dim adapt As New OleDb.OleDbDataAdapter(strSql, connection)
                If Not connection.State = ConnectionState.Open Then connection.Open()
                adapt.Fill(table)
                table.Clear()
            Catch ex As Exception
                Throw New Exception("Erreur lors du chargement de la structure de la table AUTAPP" & vbCrLf & ex.ToString)
            End Try
        End Sub
        Public Overrides Function getCommandInsert(ByRef transaction As OleDb.OleDbTransaction) As OleDb.OleDbCommand
            Try
                Dim strSql As String
                strSql = "Insert into " & strBibliothequeEtFichier & "(" _
                 & " ID , AAPCHE , AAPNOM , AAPNID " _
                 & ") values(" _
                 & " ? , ? , ? , ? )"
                Dim cmd As OleDb.OleDbCommand = connection.CreateCommand
                cmd.CommandText = strSql
                cmd.Transaction = transaction
                cmd.Parameters.Clear()
     
                cmd.Parameters.Add("iID", OleDb.OleDbType.Integer, 8, "ID")
                cmd.Parameters.Add("iAAPCHE", OleDb.OleDbType.VarChar, 100, "AAPCHE")
                cmd.Parameters.Add("iAAPNOM", OleDb.OleDbType.VarChar, 100, "AAPNOM")
                cmd.Parameters.Add("iAAPNID", OleDb.OleDbType.VarChar, 20, "AAPNID")
                Return cmd
            Catch ex As Exception
                Throw New Exception("Erreur lors de la création de la commande insert pour AUTAPP" & vbCrLf & ex.ToString, ex)
            End Try
        End Function
     
        Public Overrides Function getCommandUpdate(ByRef transaction As OleDb.OleDbTransaction) As OleDb.OleDbCommand
            Try
                Dim strSql As String
                strSql = "Update " & strBibliothequeEtFichier & " set" _
                 & " ID = ?, AAPCHE = ?, AAPNOM = ?, AAPNID = ?" _
                 & " where " _
                 & " ID = ? and AAPCHE = ? and AAPNOM = ? and AAPNID = ? "
                Dim cmd As OleDb.OleDbCommand = connection.CreateCommand
                cmd.CommandText = strSql
                cmd.Transaction = transaction
                cmd.Parameters.Clear()
     
                cmd.Parameters.Add("iID", OleDb.OleDbType.Integer, 8, "ID")
                cmd.Parameters.Add("iAAPCHE", OleDb.OleDbType.VarChar, 100, "AAPCHE")
                cmd.Parameters.Add("iAAPNOM", OleDb.OleDbType.VarChar, 100, "AAPNOM")
                cmd.Parameters.Add("iAAPNID", OleDb.OleDbType.VarChar, 20, "AAPNID")
                'Where
                cmd.Parameters.Add("oID", OleDb.OleDbType.Integer, 8, "ID").SourceVersion = DataRowVersion.Original
                cmd.Parameters.Add("oAAPCHE", OleDb.OleDbType.VarChar, 100, "AAPCHE").SourceVersion = DataRowVersion.Original
                cmd.Parameters.Add("oAAPNOM", OleDb.OleDbType.VarChar, 100, "AAPNOM").SourceVersion = DataRowVersion.Original
                cmd.Parameters.Add("oAAPNID", OleDb.OleDbType.VarChar, 20, "AAPNID").SourceVersion = DataRowVersion.Original
                Return cmd
            Catch ex As Exception
                Throw New Exception("Erreur lors de la création de la commande update pour AUTAPP" & vbCrLf & ex.ToString, ex)
            End Try
        End Function
        Public Overrides Function getCommandDelete(ByRef transaction As OleDb.OleDbTransaction) As OleDb.OleDbCommand
            Try
                Dim strSql As String
                strSql = "Delete from " & strBibliothequeEtFichier & " where" _
                 & " ID = ? and AAPCHE = ? and AAPNOM = ? and AAPNID = ? "
                Dim cmd As OleDb.OleDbCommand = connection.CreateCommand
                cmd.CommandText = strSql
                cmd.Transaction = transaction
                cmd.Parameters.Clear()
     
                cmd.Parameters.Add("oID", OleDb.OleDbType.Integer, 8, "ID").SourceVersion = DataRowVersion.Original
                cmd.Parameters.Add("oAAPCHE", OleDb.OleDbType.VarChar, 100, "AAPCHE").SourceVersion = DataRowVersion.Original
                cmd.Parameters.Add("oAAPNOM", OleDb.OleDbType.VarChar, 100, "AAPNOM").SourceVersion = DataRowVersion.Original
                cmd.Parameters.Add("oAAPNID", OleDb.OleDbType.VarChar, 20, "AAPNID").SourceVersion = DataRowVersion.Original
                Return cmd
            Catch ex As Exception
                Throw New Exception("Erreur lors de la création de la commande delete pour AUTAPP" & vbCrLf & ex.ToString, ex)
            End Try
        End Function
     
        Public Overrides Function getElement(ByRef row As System.Data.DataRow) As ClassElement
            Return New ClassElementAUTAPP(row, Me)
        End Function
     
        Public Overrides Function getNewElement() As ClassElement
            If table Is Nothing Then ChargeStructuretable()
            Return New ClassElementAUTAPP(table.NewRow, Me)
        End Function
    #End Region
     
     
    #region "Méthode de chargement"
    	'Toutes mes méthodes de chargement
    #End Region
     
    #region "Méthode de traitement"
    	'Toutes mes méthodes de traitement 
    #End Region
     
    End Class
    La fonction getElement me retournait une class nommée classElementNOMTABLE qui encapsulait une DATAROW et qui permettait un d’accéder aux éléments de la ligne directement par des propriétés. Cela permettait aussi de mettre la logique métier et de faire une sauvegarde directement sur cette class :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    Public Class ClassElementAUTAPP
        Inherits ClassElement
    #Region "Code créé par le programme G00039VB"
    #Region "Tableau"
        Public tabNomChamps As String() = { _
         "ID", "AAPCHE", "AAPNOM", "AAPNID"}
        Public tabTypeChamps As String() = { _
         "Integer", "String", "String", "String"}
        Public tabLongueurChamps As Integer() = { _
         8, 100, 100, 20}
        Public tabValMaxChamps As Decimal() = { _
         99999999, 0, 0, 0}
    #End Region
    #Region "Enumération"
        Public Enum eNomChamps
            ID = 0
            AAPCHE = 1
            AAPNOM = 2
            AAPNID = 3
        End Enum
     
        Public Enum eDescri
            ID = 0
            Chemin = 1
            Nom_textuelle = 2
            Nom_interne = 3
        End Enum
    #End Region
    #Region "Propriétées"
    #Region "Propriétées description"
     
    #Region "Bloc 0"
     
        ' 0
     
        ''' <summary>
        ''' Champs ID
        ''' </summary>
        ''' <value>Comprise entre -99999999 et 99999999</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
     
        Public Property pD_ID() As Integer
            Get
                If IsDBNull(myRow("ID")) Then Return 0
                Return myRow("ID")
            End Get
            Set(ByVal value As Integer)
                If value < -99999999 Or value > 99999999 Then Throw New ArgumentException("Doit être comprit entre -99999999 et 99999999.", "ID")
                If Not IsDBNull(myRow("ID")) Then
                    If value = myRow("ID") Then Exit Property
                End If
                myRow("ID") = value
     
            End Set
        End Property
     
        ' 1
     
        ''' <summary>
        ''' Champs AAPCHE
        ''' </summary>
        ''' <value>Maximum 100</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
     
        Public Property pD_Chemin() As String
            Get
                If IsDBNull(myRow("AAPCHE")) Then Return ""
                Return myRow("AAPCHE")
            End Get
            Set(ByVal value As String)
                If value.length > 100 Then Throw New ArgumentException("Maximum 100 charactère.", "Chemin")
                If Not IsDBNull(myRow("AAPCHE")) Then
                    If value = myRow("AAPCHE") Then Exit Property
                End If
                myRow("AAPCHE") = value
     
            End Set
        End Property
     
        ' 2
     
        ''' <summary>
        ''' Champs AAPNOM
        ''' </summary>
        ''' <value>Maximum 100</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
     
        Public Property pD_Nom_textuelle() As String
            Get
                If IsDBNull(myRow("AAPNOM")) Then Return ""
                Return myRow("AAPNOM")
            End Get
            Set(ByVal value As String)
                If value.length > 100 Then Throw New ArgumentException("Maximum 100 charactère.", "Nom_textuelle")
                If Not IsDBNull(myRow("AAPNOM")) Then
                    If value = myRow("AAPNOM") Then Exit Property
                End If
                myRow("AAPNOM") = value
     
            End Set
        End Property
     
        ' 3
     
        ''' <summary>
        ''' Champs AAPNID
        ''' </summary>
        ''' <value>Maximum 20</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
     
        Public Property pD_Nom_interne() As String
            Get
                If IsDBNull(myRow("AAPNID")) Then Return ""
                Return myRow("AAPNID")
            End Get
            Set(ByVal value As String)
                If value.length > 20 Then Throw New ArgumentException("Maximum 20 charactère.", "Nom_interne")
                If Not IsDBNull(myRow("AAPNID")) Then
                    If value = myRow("AAPNID") Then Exit Property
                End If
                myRow("AAPNID") = value
     
            End Set
        End Property
     
    #End Region
     
    #End Region
     
    #Region "Propriété NOM"
     
    #Region "Bloc 0"
     
        ' 0
     
        ''' <summary>
        ''' Description : ID
        ''' </summary>
        ''' <value>Comprise entre -99999999 et 99999999</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
     
        Public Property pN_ID() As Integer
            Get
                If IsDBNull(myRow("ID")) Then Return 0
                Return myRow("ID")
            End Get
            Set(ByVal value As Integer)
                If value < -99999999 Or value > 99999999 Then Throw New ArgumentException("Doit être comprit entre -99999999 et 99999999.", "ID")
                If Not IsDBNull(myRow("ID")) Then
                    If value = myRow("ID") Then Exit Property
                End If
                myRow("ID") = value
     
            End Set
        End Property
     
        ' 1
     
        ''' <summary>
        ''' Description : Chemin
        ''' </summary>
        ''' <value>Maximum 100</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
     
        Public Property pN_AAPCHE() As String
            Get
                If IsDBNull(myRow("AAPCHE")) Then Return ""
                Return myRow("AAPCHE")
            End Get
            Set(ByVal value As String)
                If value.length > 100 Then Throw New ArgumentException("Maximum 100 charactère.", "Chemin")
                If Not IsDBNull(myRow("AAPCHE")) Then
                    If value = myRow("AAPCHE") Then Exit Property
                End If
                myRow("AAPCHE") = value
     
            End Set
        End Property
     
        ' 2
     
        ''' <summary>
        ''' Description : Nom textuelle
        ''' </summary>
        ''' <value>Maximum 100</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
     
        Public Property pN_AAPNOM() As String
            Get
                If IsDBNull(myRow("AAPNOM")) Then Return ""
                Return myRow("AAPNOM")
            End Get
            Set(ByVal value As String)
                If value.length > 100 Then Throw New ArgumentException("Maximum 100 charactère.", "Nom_textuelle")
                If Not IsDBNull(myRow("AAPNOM")) Then
                    If value = myRow("AAPNOM") Then Exit Property
                End If
                myRow("AAPNOM") = value
     
            End Set
        End Property
     
        ' 3
     
        ''' <summary>
        ''' Description : Nom interne
        ''' </summary>
        ''' <value>Maximum 20</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
     
        Public Property pN_AAPNID() As String
            Get
                If IsDBNull(myRow("AAPNID")) Then Return ""
                Return myRow("AAPNID")
            End Get
            Set(ByVal value As String)
                If value.length > 20 Then Throw New ArgumentException("Maximum 20 charactère.", "Nom_interne")
                If Not IsDBNull(myRow("AAPNID")) Then
                    If value = myRow("AAPNID") Then Exit Property
                End If
                myRow("AAPNID") = value
     
            End Set
        End Property
     
    #End Region
     
    #End Region
        Public Property GetChamp(ByVal iNomChamps As Integer) As Object
            Get
                Select Case tabTypeChamps(iNomChamps)
                    Case "String"
                        If IsDBNull(myRow(tabNomChamps(iNomChamps))) Then Return ""
                    Case Else
                        If IsDBNull(myRow(tabNomChamps(iNomChamps))) Then Return 0
                End Select
                Return myRow(tabNomChamps(iNomChamps))
            End Get
            Set(ByVal value As Object)
                If Not IsDBNull(myRow(tabNomChamps(iNomChamps))) Then
                    If value = myRow(tabNomChamps(iNomChamps)) Then Exit Property
                End If
                Select Case tabTypeChamps(iNomChamps)
                    Case "String"
                        If value.length > tabLongueurChamps(iNomChamps) Then Throw New ArgumentException("Maximum " & tabLongueurChamps(iNomChamps) & " charactère.", tabNomChamps(iNomChamps))
                    Case Else
                        If value < -tabValMaxChamps(iNomChamps) Or value > tabValMaxChamps(iNomChamps) Then Throw New ArgumentException("Doit être comprit entre -" & tabValMaxChamps(iNomChamps) & " et " & tabValMaxChamps(iNomChamps) & ".", tabNomChamps(iNomChamps))
                End Select
                myRow(tabNomChamps(iNomChamps)) = value
            End Set
        End Property
    #End Region
        Public Sub New(ByRef row As DataRow, ByRef parent As ClassDonnee)
            MyBase.New(row, parent)
            ' Initialisation de tout les champs.
            For i As Integer = 0 To tabNomChamps.Length - 1
                GetChamp(i) = GetChamp(i)
            Next
        End Sub
    #End Region
     
    #region "Méthode de traitement"
    	'Toutes mes méthodes de traitement 
    #End Region
    End Class
    J'étais très loin du niveau des articles mais j'avais mi ça en place au début de ma carrière, il est vraiment temps de la réécrire


    Tout ça pour te montrer pourquoi je cherchais à avoir une liste d'objet métier POCO au lieu de simple DTO.
    Une de mes applications gère l'impression automatique de courrier créé la veille.
    Le programme groupe les impressions par type et vu que le type dépend de plusieurs champs, je m'amuse à faire des DATATABLE.SELECT qui doivent connaitre les différents champs. La collection de DTO ne changerait rien à ce problème car je devrais faire des requêtes Linq avec connaissance de ces différents champs et donc de la logique BD.
    Par contre si j'utilisais une collection de POCO, je pourrais avoir des propriétés calculées du genre Service, Recommande, etc et faire ma requête Linq directement sur ces propriétés sans connaitre la logique BD qui serait dans le POCO.

    Citation Envoyé par rv26t Voir le message
    Et que M. Lacovara ne donne pas plus d'exemple.
    Mais il me semble que l'ensemble POCO + DTO va correspondre a des gestions relativement simples, mais que lorsque l'on va commencer a avoir des gestions complexe dans la BLL, cela va devenir un casse-tête.
    En voyant le terme BLL, j'ai recherché ça définition et je suis tombé sur ça.
    Donc maintenant je dirais que la collection de DTO que tu voulais mettre dans un POCO devrais à mon point de vue se trouver dans la BLL (même si moi je préférerais avoir une collection de POCO) qui contiendra la logique métier.

    Je sens qu'on se rapproche d'une meilleur compréhension, je vais encore approfondir les différents liens que je trouve au sujet d'application avec BLL, DAL et POCO.

  3. #23
    Expert confirmé
    Avatar de Kropernic
    Homme Profil pro
    Analyste / Programmeur / DBA
    Inscrit en
    Juillet 2006
    Messages
    3 932
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Belgique

    Informations professionnelles :
    Activité : Analyste / Programmeur / DBA
    Secteur : Distribution

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 932
    Points : 4 239
    Points
    4 239
    Par défaut
    Omg ! Vous m'avez complètement largué .

    Pour quelqu'un qui n'a jamais eu de cours de POO ni d'architecture, ça va trop vite ^^. (et je n'ai pas le temps de tout relire plusieurs fois, j'ai un projet à boucler en un temps record)

    Je compte sur Sankass pour me faire une résumé à la fin (avec un article sur le blog ? ) ^^
    Kropernic

  4. #24
    Membre éprouvé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2006
    Messages
    436
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2006
    Messages : 436
    Points : 963
    Points
    963
    Par défaut
    Sankass :
    Citation Envoyé par Kikuts
    Perso, je n'utilise plus les POCO depuis l'apparition des Portable Class Library : je préfère avoir une DLL qui pourra s'intégrer un peu partout qui peut valider soit un DTO soit une interface (un DTO implémente 1,N interfaces) du coup je peux valider une partie ou l'intégralité d'un DTO, d'un model Silverlight, d'un model WindowsPhone etc.

    Réutilisabilité côté client serveur et multi projet. Bien entendu on peut aussi faire ça avec les POCO. Mais pour valider un model qui reste côté projet UI, mes HelperValidation ou ServiceValidation apporte beaucoup plus.
    Je pense que l'on transgresse de nouveau le terme DTO dans ce cas vu que c'est censé être l'objet le plus léger possible il ne devrait pas implémenter d'interface et donc avoir une partie métier. Donc dans ce cas ce que tu considère comme DTO est en fait un POCO donc ton modèle correspond à celui de youtpout978 non ?
    Je ne vois pas en quoi ça transgresse quelconque principe car mes interfaces ne contiennent que les champs (property) et aucune signature de méthode ! donc rien d'autres que des données à transférer.

    PCL = portable Class Livrary = DLL que tu peux importer dans tout type de projet du coup tes DTO tu peux les imports en l'état côté client, serveur, validation etc. Et à partir de là, les POCO perdent en intérêt pour le coup en perf que ça ajoute (même si c'est ultra light ça joue un peu quand même)
    "S'adapter, c'est vaincre" - Cellendhyll de Cortavar

  5. #25
    Modérateur
    Avatar de Sankasssss
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Novembre 2006
    Messages
    1 842
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 842
    Points : 4 232
    Points
    4 232
    Par défaut
    @Kropernic : Avant de me lancer la dedans il faudrait m'assurer que je comprenne bien la différence entre chaque type d'objet

    @Kikuts : Ok pour les intefaces, on reste effectivement dans la définition du DTO.
    Mais concernant la logique métier, tu la met où?
    Car c'est principalement là que nous nous embrouillons visiblement dans la discussion. rv26t crée un seul POCO qui contiendra une liste de DTO et qui aura des méthodes de traitement. Du coup j'ai l'impression que son objet ne doit plus être considéré comme un POCO mais autre.
    Moi je vois une collection de POCO contenant chacun la logique métier qui agirait sur son DTO (un POCO = un DTO), cette collection étant stockée dans un autre objet (de la couche BLL ??? ) qui elle contient des méthodes agissant sur la collection de POCO.

    P.S. : Si tu as des ressources traitant des bonnes pratiques on est preneur

  6. #26
    Expert confirmé

    Homme Profil pro
    Développeur .NET
    Inscrit en
    Novembre 2010
    Messages
    2 065
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Novembre 2010
    Messages : 2 065
    Points : 4 229
    Points
    4 229
    Par défaut
    Dans le cas où on a besoin d'utiliser des POCO, quel est le réel plus à utiliser des DTO ?
    Je ne vois pas ce qu'ils apportent à part une classe en plus à coder, je pose la question parce que je n'ai encore jamais vu de projet en utiliser.

  7. #27
    Modérateur
    Avatar de Sankasssss
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Novembre 2006
    Messages
    1 842
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 842
    Points : 4 232
    Points
    4 232
    Par défaut
    L'avantage que je comprend au fil de mes lectures et comme le fait remarquer Kikuts serait en performance. Principalement quand on les fait transiter sur le réseau.

  8. #28
    Membre émérite Avatar de meziantou
    Homme Profil pro
    Ingénieur R&D
    Inscrit en
    Avril 2010
    Messages
    1 223
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Canada

    Informations professionnelles :
    Activité : Ingénieur R&D
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2010
    Messages : 1 223
    Points : 2 439
    Points
    2 439
    Par défaut
    @Sankasssss : En quoi les DTO seraient plus rapide à faire transiter sur le réseau?


    J'ai beaucoup de mal à comprendre l'intérêt des POCO et DTO.
    En gros si j'ai bien compris vous mettez toutes les données dans les DTO, toutes les méthodes dans les POCO et un POCO contient un DTO (donc les données).
    Pourquoi ne pas tout mettre dans la même entité : les données et les traitements ? Après tout, n'est-ce pas le concept de la programmation orienté objet ?

    L'outil que j'utilise génère un code de la sorte avec lequel je suis très satisfait (désolé d'avance pour ce code en C# sur le forum VB.NET) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    478
    479
    480
    481
    482
    483
    484
    485
    486
    487
    488
    489
    490
    491
    492
    493
    494
    495
    496
    497
    498
    499
    500
    501
    502
    503
    504
    505
    506
    507
    508
    509
    510
    511
    512
    513
    514
    515
    516
    517
    518
    519
    520
    521
    522
    523
    524
    525
    526
    527
    528
    529
    530
    531
    532
    533
    534
    535
    536
    537
    538
    539
    540
    541
    542
    543
    544
    545
    546
    547
    548
    549
    550
    551
    552
    553
    554
    555
    556
    557
    558
    559
    560
    561
    562
    563
    564
    565
    566
    567
    568
    569
    570
    571
    572
    573
    574
    575
    576
    577
    578
    579
    580
    581
    582
    583
    584
    585
    586
    587
    588
    589
    590
    591
    592
    593
    594
    595
    596
    597
    598
    599
    600
    601
    602
    603
    604
    605
    606
    607
    608
    609
    610
    611
    612
    613
    614
    615
    616
    617
    618
    619
    620
    621
    622
    623
    624
    625
    626
    627
    628
    629
    630
    631
    632
    633
    634
    635
    636
    637
    638
    639
    640
    641
    642
    643
    644
    645
    646
    647
    648
    649
    650
    651
    652
    653
    654
    655
    656
    657
    658
    659
    660
    661
    662
    663
    664
    665
    666
    667
    668
    669
    670
    671
    672
    673
    674
    675
    676
    677
    678
    679
    680
    681
    682
    683
    684
    685
    686
    687
    688
    689
    690
    691
    692
    693
    694
    695
    696
    697
    698
    699
    700
    701
    702
    703
    704
    705
    706
    707
    708
    709
    710
    711
    712
    713
    714
    715
    [System.SerializableAttribute()]
    [System.ComponentModel.DataObjectAttribute()]
    [System.Diagnostics.DebuggerDisplayAttribute("EK={EntityKey}, Name={Name}, Id={Id}")]
    [System.ComponentModel.TypeConverterAttribute(typeof(CodeFluent.Runtime.Design.NameTypeConverter))]
    public partial class Customer : System.ICloneable, System.IComparable, System.IComparable<Model1.Customer>, CodeFluent.Runtime.ICodeFluentCollectionEntity<int>, System.ComponentModel.IDataErrorInfo, CodeFluent.Runtime.ICodeFluentMemberValidator, CodeFluent.Runtime.ICodeFluentSummaryValidator, System.IEquatable<Model1.Customer>
    {    
        private bool _raisePropertyChangedEvents = true;    
        private CodeFluent.Runtime.CodeFluentEntityState _entityState;    
        private byte[] _rowVersion;    
        private int _id = -1;    
        private string _name = default(string);
     
        [System.NonSerializedAttribute()]
        private static CodeFluent.Runtime.Rules.StringValidator _nameValidator = new CodeFluent.Runtime.Rules.StringValidator(Model1.Resources.ValueValidationFailureHandler.Current, 1, 50, default(string), default(CodeFluent.Runtime.Rules.StringValidatorOptions));
     
        public Customer()
        {
            this._entityState = CodeFluent.Runtime.CodeFluentEntityState.Created;
        }
     
        [System.ComponentModel.BrowsableAttribute(false)]
        [System.Xml.Serialization.XmlIgnoreAttribute()]
        public virtual bool RaisePropertyChangedEvents
        {
            get
            {
                return this._raisePropertyChangedEvents;
            }
            set
            {
                this._raisePropertyChangedEvents = value;
            }
        }
     
        public virtual string EntityKey
        {
            get
            {
                return this.Id.ToString();
            }
            set
            {
                this.Id = ((int)(ConvertUtilities.ChangeType(value, typeof(int), -1)));
            }
        }
     
        public virtual string EntityDisplayName
        {
            get
            {
                return this.Name;
            }
        }
     
        [System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
        [System.ComponentModel.DataObjectFieldAttribute(false, true)]
        [System.ComponentModel.TypeConverterAttribute(typeof(CodeFluent.Runtime.Design.ByteArrayConverter))]
        public byte[] RowVersion
        {
            get
            {
                return this._rowVersion;
            }
            set
            {
                if (((value != null) && (value.Length == 0)))
                {
                    value = null;
                }
                this._rowVersion = value;
                this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("RowVersion"));
            }
        }
     
        [System.ComponentModel.DefaultValueAttribute(((int)(-1)))]
        [System.Xml.Serialization.XmlElementAttribute(IsNullable=false, Type=typeof(int))]
        [System.ComponentModel.DataObjectFieldAttribute(true)]
        public int Id
        {
            get
            {
                return this._id;
            }
            set
            {
                if ((System.Collections.Generic.EqualityComparer<int>.Default.Equals(value, this._id) == true))
                {
                    return;
                }
                int oldKey = this._id;
                this._id = value;
                try
                {
                    this.OnCollectionKeyChanged(oldKey);
                }
                catch (System.ArgumentException )
                {
                    this._id = oldKey;
                    return;
                }
                this.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Modified;
                this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("Id"));
            }
        }
     
        [System.ComponentModel.DefaultValueAttribute(default(string))]
        [System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Type=typeof(string))]
        public string Name
        {
            get
            {
                return this._name;
            }
            set
            {
                this._name = value;
                this.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Modified;
                this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("Name"));
            }
        }
     
        protected static CodeFluent.Runtime.Rules.StringValidator NameValidator
        {
            get
            {
                return Model1.Customer._nameValidator;
            }
        }
     
        string System.ComponentModel.IDataErrorInfo.Error
        {
            get
            {
                return this.Validate(System.Globalization.CultureInfo.CurrentCulture);
            }
        }
     
        string System.ComponentModel.IDataErrorInfo.this[string columnName]
        {
            get
            {
                return CodeFluentPersistence.ValidateMember(System.Globalization.CultureInfo.CurrentCulture, this, columnName, null);
            }
        }
     
        int CodeFluent.Runtime.Utilities.IKeyable<System.Int32>.Key
        {
            get
            {
                return this.Id;
            }
        }
     
        public virtual CodeFluent.Runtime.CodeFluentEntityState EntityState
        {
            get
            {
                return this._entityState;
            }
            set
            {
                if ((System.Collections.Generic.EqualityComparer<CodeFluent.Runtime.CodeFluentEntityState>.Default.Equals(value, this.EntityState) == true))
                {
                    return;
                }
                if (((this._entityState == CodeFluent.Runtime.CodeFluentEntityState.ToBeDeleted) 
                            && (value == CodeFluent.Runtime.CodeFluentEntityState.Modified)))
                {
                    return;
                }
                if (((this._entityState == CodeFluent.Runtime.CodeFluentEntityState.Created) 
                            && (value == CodeFluent.Runtime.CodeFluentEntityState.Modified)))
                {
                    return;
                }
                this._entityState = value;
                this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("EntityState"));
            }
        }
     
        [field:System.NonSerializedAttribute()]
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
     
        [field:System.NonSerializedAttribute()]
        public event CodeFluent.Runtime.CodeFluentEntityActionEventHandler EntityAction;
     
        [field:System.NonSerializedAttribute()]
        public event System.EventHandler<CodeFluent.Runtime.Utilities.KeyChangedEventArgs<int>> KeyChanged;
     
        protected virtual void OnPropertyChanged(System.ComponentModel.PropertyChangedEventArgs e)
        {
            if ((this.RaisePropertyChangedEvents == false))
            {
                return;
            }
            if ((this.PropertyChanged != null))
            {
                this.PropertyChanged(this, e);
            }
        }
     
        protected virtual void OnEntityAction(CodeFluent.Runtime.CodeFluentEntityActionEventArgs e)
        {
            if ((this.EntityAction != null))
            {
                this.EntityAction(this, e);
            }
        }
     
        public virtual bool Equals(Model1.Customer customer)
        {
            if ((customer == null))
            {
                return false;
            }
            if ((this.Id == -1))
            {
                return base.Equals(customer);
            }
            return (this.Id.Equals(customer.Id) == true);
        }
     
        public override int GetHashCode()
        {
            return this._id;
        }
     
        public override bool Equals(object obj)
        {
            Model1.Customer customer = null;
    		customer = obj as Model1.Customer;
            return this.Equals(customer);
        }
     
        int System.IComparable.CompareTo(object value)
        {
            Model1.Customer customer = null;
    		customer = value as Model1.Customer;
            if ((customer == null))
            {
                throw new System.ArgumentException("value");
            }
            int localCompareTo = this.CompareTo(customer);
            return localCompareTo;
        }
     
        public virtual int CompareTo(Model1.Customer customer)
        {
            if ((customer == null))
            {
                throw new System.ArgumentNullException("customer");
            }
            int localCompareTo = this.Id.CompareTo(customer.Id);
            return localCompareTo;
        }
     
        public virtual string Validate(System.Globalization.CultureInfo culture)
        {
            return CodeFluentPersistence.Validate(culture, this, null);
        }
     
        public virtual void Validate(System.Globalization.CultureInfo culture, System.Collections.Generic.IList<CodeFluent.Runtime.CodeFluentValidationException> results)
        {
            CodeFluent.Runtime.CodeFluentEntityActionEventArgs evt = new CodeFluent.Runtime.CodeFluentEntityActionEventArgs(this, CodeFluent.Runtime.CodeFluentEntityAction.Validating, true, results);
            evt.Culture = culture;
            this.OnEntityAction(evt);
            if ((evt.Cancel == true))
            {
                string externalValidate;
                if ((evt.Argument != null))
                {
                    externalValidate = evt.Argument.ToString();
                }
                else
                {
                    externalValidate = Model1.Resources.Manager.GetStringWithDefault(culture, "Model1.Customer.ExternalValidate", "Type \'Model1.Customer\' cannot be validated.", null);
                }
                CodeFluentPersistence.AddValidationError(results, externalValidate);
            }
            CodeFluentPersistence.ValidateMember(culture, results, this, null);
            if ((this.Name == default(string)))
            {
                string localValidate = Model1.Resources.Manager.GetStringWithDefault(culture, "Model1.Customer.Name.NullException", "\'Name\' property cannot be set to \'\' for type \'Model1.Customer\'", null);
                CodeFluentPersistence.AddValidationError(results, localValidate);
            }
            this.OnEntityAction(new CodeFluent.Runtime.CodeFluentEntityActionEventArgs(this, CodeFluent.Runtime.CodeFluentEntityAction.Validated, false, results));
        }
     
        public void Validate()
        {
            string var = this.Validate(System.Globalization.CultureInfo.CurrentCulture);
            if ((var != null))
            {
                throw new CodeFluent.Runtime.CodeFluentValidationException(var);
            }
        }
     
        string CodeFluent.Runtime.ICodeFluentValidator.Validate(System.Globalization.CultureInfo culture)
        {
            string localValidate = this.Validate(culture);
            return localValidate;
        }
     
        void CodeFluent.Runtime.ICodeFluentMemberValidator.Validate(System.Globalization.CultureInfo culture, string memberName, System.Collections.Generic.IList<CodeFluent.Runtime.CodeFluentValidationException> results)
        {
            this.ValidateMember(culture, memberName, results);
        }
     
        public virtual bool Delete()
        {
            bool ret = false;
            CodeFluent.Runtime.CodeFluentEntityActionEventArgs evt = new CodeFluent.Runtime.CodeFluentEntityActionEventArgs(this, CodeFluent.Runtime.CodeFluentEntityAction.Deleting, true);
            this.OnEntityAction(evt);
            if ((evt.Cancel == true))
            {
                return ret;
            }
            if ((this.EntityState == CodeFluent.Runtime.CodeFluentEntityState.Deleted))
            {
                return ret;
            }
            if ((this.RowVersion == null))
            {
                return ret;
            }
            CodeFluent.Runtime.CodeFluentPersistence persistence = CodeFluentContext.Get(Model1.Constants.Model1StoreName).Persistence;
            persistence.CreateStoredProcedureCommand(null, "Customer", "Delete");
            persistence.AddParameter("@Customer_Id", this.Id, ((int)(-1)));
            persistence.AddParameter("@_rowVersion", this.RowVersion);
            persistence.ExecuteNonQuery();
            this.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Deleted;
            this.OnEntityAction(new CodeFluent.Runtime.CodeFluentEntityActionEventArgs(this, CodeFluent.Runtime.CodeFluentEntityAction.Deleted, false, false));
            ret = true;
            return ret;
        }
     
        protected virtual void ReadRecord(System.Data.IDataReader reader, CodeFluent.Runtime.CodeFluentReloadOptions options)
        {
            if ((reader == null))
            {
                throw new System.ArgumentNullException("reader");
            }
            if ((((options & CodeFluent.Runtime.CodeFluentReloadOptions.Properties) 
                        == 0) 
                        == false))
            {
                this._id = CodeFluentPersistence.GetReaderValue(reader, "Customer_Id", ((int)(-1)));
                this._name = CodeFluentPersistence.GetReaderValue(reader, "Customer_Name", ((string)(default(string))));
            }
            if ((((options & CodeFluent.Runtime.CodeFluentReloadOptions.RowVersion) 
                        == 0) 
                        == false))
            {
                this._rowVersion = CodeFluentPersistence.GetReaderValue(reader, "_rowVersion", ((byte[])(null)));
            }
            this.OnEntityAction(new CodeFluent.Runtime.CodeFluentEntityActionEventArgs(this, CodeFluent.Runtime.CodeFluentEntityAction.ReadRecord, false, false));
        }
     
        void CodeFluent.Runtime.ICodeFluentEntity.ReadRecord(System.Data.IDataReader reader)
        {
            this.ReadRecord(reader, CodeFluent.Runtime.CodeFluentReloadOptions.Default);
        }
     
        protected virtual void ReadRecordOnSave(System.Data.IDataReader reader)
        {
            if ((reader == null))
            {
                throw new System.ArgumentNullException("reader");
            }
            this._id = CodeFluentPersistence.GetReaderValue(reader, "Customer_Id", ((int)(-1)));
            this._rowVersion = CodeFluentPersistence.GetReaderValue(reader, "_rowVersion", ((byte[])(null)));
            this.OnEntityAction(new CodeFluent.Runtime.CodeFluentEntityActionEventArgs(this, CodeFluent.Runtime.CodeFluentEntityAction.ReadRecordOnSave, false, false));
        }
     
        void CodeFluent.Runtime.ICodeFluentEntity.ReadRecordOnSave(System.Data.IDataReader reader)
        {
            this.ReadRecordOnSave(reader);
        }
     
        [System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Select, true)]
        public static Model1.Customer Load(int id)
        {
            if ((id == -1))
            {
                return null;
            }
            Model1.Customer customer = new Model1.Customer();
            CodeFluent.Runtime.CodeFluentPersistence persistence = CodeFluentContext.Get(Model1.Constants.Model1StoreName).Persistence;
            persistence.CreateStoredProcedureCommand(null, "Customer", "Load");
            persistence.AddParameter("@Id", id, ((int)(-1)));
            System.Data.IDataReader reader = null;
            try
            {
                reader = persistence.ExecuteReader();
                if ((reader.Read() == true))
                {
                    customer.ReadRecord(reader, CodeFluent.Runtime.CodeFluentReloadOptions.Default);
                    customer.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Unchanged;
                    return customer;
                }
            }
            finally
            {
                if ((reader != null))
                {
                    reader.Dispose();
                }
                persistence.CompleteCommand();
            }
            return null;
        }
     
        [System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Select, false)]
        public static Model1.Customer LoadById(int id)
        {
            if ((id == -1))
            {
                return null;
            }
            Model1.Customer customer = new Model1.Customer();
            CodeFluent.Runtime.CodeFluentPersistence persistence = CodeFluentContext.Get(Model1.Constants.Model1StoreName).Persistence;
            persistence.CreateStoredProcedureCommand(null, "Customer", "LoadById");
            persistence.AddParameter("@Id", id, ((int)(-1)));
            System.Data.IDataReader reader = null;
            try
            {
                reader = persistence.ExecuteReader();
                if ((reader.Read() == true))
                {
                    customer.ReadRecord(reader, CodeFluent.Runtime.CodeFluentReloadOptions.Default);
                    customer.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Unchanged;
                    return customer;
                }
            }
            finally
            {
                if ((reader != null))
                {
                    reader.Dispose();
                }
                persistence.CompleteCommand();
            }
            return null;
        }
     
        [System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Select, false)]
        public static Model1.Customer LoadByName(string name)
        {
            if ((name == default(string)))
            {
                return null;
            }
            Model1.Customer customer = new Model1.Customer();
            CodeFluent.Runtime.CodeFluentPersistence persistence = CodeFluentContext.Get(Model1.Constants.Model1StoreName).Persistence;
            persistence.CreateStoredProcedureCommand(null, "Customer", "LoadByName");
            persistence.AddParameter("@Name", name, default(string));
            System.Data.IDataReader reader = null;
            try
            {
                reader = persistence.ExecuteReader();
                if ((reader.Read() == true))
                {
                    customer.ReadRecord(reader, CodeFluent.Runtime.CodeFluentReloadOptions.Default);
                    customer.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Unchanged;
                    return customer;
                }
            }
            finally
            {
                if ((reader != null))
                {
                    reader.Dispose();
                }
                persistence.CompleteCommand();
            }
            return null;
        }
     
        public virtual bool Reload(CodeFluent.Runtime.CodeFluentReloadOptions options)
        {
            bool ret = false;
            if ((this.Id == -1))
            {
                return ret;
            }
            CodeFluent.Runtime.CodeFluentPersistence persistence = CodeFluentContext.Get(Model1.Constants.Model1StoreName).Persistence;
            persistence.CreateStoredProcedureCommand(null, "Customer", "Load");
            persistence.AddParameter("@Id", this.Id, ((int)(-1)));
            System.Data.IDataReader reader = null;
            try
            {
                reader = persistence.ExecuteReader();
                if ((reader.Read() == true))
                {
                    this.ReadRecord(reader, options);
                    this.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Unchanged;
                    ret = true;
                }
                else
                {
                    this.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Deleted;
                }
            }
            finally
            {
                if ((reader != null))
                {
                    reader.Dispose();
                }
                persistence.CompleteCommand();
            }
            return ret;
        }
     
        protected virtual bool BaseSave(bool force)
        {
            if ((this.EntityState == CodeFluent.Runtime.CodeFluentEntityState.ToBeDeleted))
            {
                this.Delete();
                return false;
            }
            CodeFluent.Runtime.CodeFluentEntityActionEventArgs evt = new CodeFluent.Runtime.CodeFluentEntityActionEventArgs(this, CodeFluent.Runtime.CodeFluentEntityAction.Saving, true);
            this.OnEntityAction(evt);
            if ((evt.Cancel == true))
            {
                return false;
            }
            CodeFluentPersistence.ThrowIfDeleted(this);
            this.Validate();
            if (((force == false) && (this.EntityState == CodeFluent.Runtime.CodeFluentEntityState.Unchanged)))
            {
                return false;
            }
            CodeFluent.Runtime.CodeFluentPersistence persistence = CodeFluentContext.Get(Model1.Constants.Model1StoreName).Persistence;
            persistence.CreateStoredProcedureCommand(null, "Customer", "Save");
            persistence.AddParameter("@Customer_Id", this.Id, ((int)(-1)));
            persistence.AddParameter("@Customer_Name", this.Name, default(string));
            persistence.AddParameter("@_trackLastWriteUser", persistence.Context.User.Name);
            persistence.AddParameter("@_rowVersion", this.RowVersion);
            System.Data.IDataReader reader = null;
            try
            {
                reader = persistence.ExecuteReader();
                if ((reader.Read() == true))
                {
                    this.ReadRecordOnSave(reader);
                }
                CodeFluentPersistence.NextResults(reader);
            }
            finally
            {
                if ((reader != null))
                {
                    reader.Dispose();
                }
                persistence.CompleteCommand();
            }
            this.OnEntityAction(new CodeFluent.Runtime.CodeFluentEntityActionEventArgs(this, CodeFluent.Runtime.CodeFluentEntityAction.Saved, false, false));
            this.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Unchanged;
            return true;
        }
     
        public virtual bool Save()
        {
            bool localSave = this.BaseSave(false);
            return localSave;
        }
     
        [System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Update, true)]
        public static bool Save(Model1.Customer customer)
        {
            if ((customer == null))
            {
                return false;
            }
            bool ret = customer.Save();
            return ret;
        }
     
        [System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Insert, true)]
        public static bool Insert(Model1.Customer customer)
        {
            bool ret = Model1.Customer.Save(customer);
            return ret;
        }
     
        [System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Delete, true)]
        public static bool Delete(Model1.Customer customer)
        {
            if ((customer == null))
            {
                return false;
            }
            bool ret = customer.Delete();
            return ret;
        }
     
        public string Trace()
        {
            System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder();
            System.IO.StringWriter stringWriter = new System.IO.StringWriter(stringBuilder, System.Globalization.CultureInfo.CurrentCulture);
            System.CodeDom.Compiler.IndentedTextWriter writer = new System.CodeDom.Compiler.IndentedTextWriter(stringWriter);
            this.BaseTrace(writer);
            writer.Flush();
            ((System.IDisposable)(writer)).Dispose();
            ((System.IDisposable)(stringWriter)).Dispose();
            string sr = stringBuilder.ToString();
            return sr;
        }
     
        void CodeFluent.Runtime.ICodeFluentObject.Trace(System.CodeDom.Compiler.IndentedTextWriter writer)
        {
            this.BaseTrace(writer);
        }
     
        protected virtual void BaseTrace(System.CodeDom.Compiler.IndentedTextWriter writer)
        {
            writer.Write("[");
            writer.Write("Id=");
            writer.Write(this.Id);
            writer.Write(",");
            writer.Write("Name=");
            writer.Write(this.Name);
            writer.Write(", EntityState=");
            writer.Write(this.EntityState);
            writer.Write("]");
        }
     
        [System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Select, true)]
        public static Model1.Customer LoadByEntityKey(string key)
        {
            if ((key == string.Empty))
            {
                return null;
            }
            Model1.Customer customer;
            int var = ((int)(ConvertUtilities.ChangeType(key, typeof(int), -1)));
            customer = Model1.Customer.Load(var);
            return customer;
        }
     
        protected virtual void ValidateMember(System.Globalization.CultureInfo culture, string memberName, System.Collections.Generic.IList<CodeFluent.Runtime.CodeFluentValidationException> results)
        {
            if (((memberName == "Name") || (memberName == null)))
            {
                CodeFluent.Runtime.Rules.ValueValidator.Validate(this, culture, "Name", results, Model1.Customer.NameValidator0, this.Name);
            }
        }
     
        public Model1.Customer Clone(bool deep)
        {
            Model1.Customer customer = new Model1.Customer();
            this.CopyTo(customer, deep);
            return customer;
        }
     
        public Model1.Customer Clone()
        {
            Model1.Customer localClone = this.Clone(true);
            return localClone;
        }
     
        object System.ICloneable.Clone()
        {
            object localClone = this.Clone();
            return localClone;
        }
     
        public virtual void CopyFrom(System.Collections.IDictionary dict, bool deep)
        {
            if ((dict == null))
            {
                throw new System.ArgumentNullException("dict");
            }
            if ((dict.Contains("Id") == true))
            {
                this.Id = ((int)(ConvertUtilities.ChangeType(dict["Id"], typeof(int), -1)));
            }
            if ((dict.Contains("Name") == true))
            {
                this.Name = ((string)(ConvertUtilities.ChangeType(dict["Name"], typeof(string), default(string))));
            }
            this.OnEntityAction(new CodeFluent.Runtime.CodeFluentEntityActionEventArgs(this, CodeFluent.Runtime.CodeFluentEntityAction.CopyFrom, false, dict));
        }
     
        public virtual void CopyTo(Model1.Customer customer, bool deep)
        {
            if ((customer == null))
            {
                throw new System.ArgumentNullException("customer");
            }
            customer.Id = this.Id;
            customer.Name = this.Name;
            this.OnEntityAction(new CodeFluent.Runtime.CodeFluentEntityActionEventArgs(this, CodeFluent.Runtime.CodeFluentEntityAction.CopyTo, false, customer));
        }
     
        public virtual void CopyTo(System.Collections.IDictionary dict, bool deep)
        {
            if ((dict == null))
            {
                throw new System.ArgumentNullException("dict");
            }
            dict["Id"] = this.Id;
            dict["Name"] = this.Name;
            this.OnEntityAction(new CodeFluent.Runtime.CodeFluentEntityActionEventArgs(this, CodeFluent.Runtime.CodeFluentEntityAction.CopyTo, false, dict));
        }
     
        protected void OnCollectionKeyChanged(int key)
        {
            if ((this.KeyChanged != null))
            {
                this.KeyChanged(this, new CodeFluent.Runtime.Utilities.KeyChangedEventArgs<int>(key));
            }
        }
    }
    J'essaye de comprendre mais je ne vois pas en quoi les POCO et DTO pourraient améliorer cette façon de faire ? (Je suis preneur de toutes remarques )

  9. #29
    Expert confirmé

    Homme Profil pro
    Développeur .NET
    Inscrit en
    Novembre 2010
    Messages
    2 065
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Novembre 2010
    Messages : 2 065
    Points : 4 229
    Points
    4 229
    Par défaut
    Citation Envoyé par Sankasssss Voir le message
    L'avantage que je comprend au fil de mes lectures et comme le fait remarquer Kikuts serait en performance. Principalement quand on les fait transiter sur le réseau.
    Quand on passe par un webservice on peut facilement définir quel propriété on veut transmettre par des attributs.

    Meziantou, ta classe à l'air de tout contenir, c'est assez brouillon pas franchement très lisible et je pense qu'il y a énormément de méthode qui pourrait être rendu générique.
    Si tu veux ajouter une fonctionnalité qui sera utilisée par toutes tes classes tu la code pas dans toutes tes classes tu la code une fois pour qu'elle soit exploitable par toutes tes classes.
    Bon après c'est du code généré par un outil tiers mais faire ça soit même est difficilement réalisable ça demande énormément de temps pour chaque classe et j'aimerai pas passer derrière en cas de bug.

  10. #30
    Membre émérite Avatar de meziantou
    Homme Profil pro
    Ingénieur R&D
    Inscrit en
    Avril 2010
    Messages
    1 223
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Canada

    Informations professionnelles :
    Activité : Ingénieur R&D
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2010
    Messages : 1 223
    Points : 2 439
    Points
    2 439
    Par défaut
    Que vois-tu de générique ?
    Propriétés : Id, Name, RowVersion => Pas générique
    Méthodes : Validation, Save, Load, Delete, CopyFrom, CopyTo, Equals => Pas générique à moins de faire de la reflection
    EntityState, PropertyChanged => Commun donc oui on peut factoriser un peu mais quel d'intérêt pour si peu

    c'est assez brouillon pas franchement très lisible
    Je t'assure dans Visual Studio c'est tout de suite plus lisible

    Si tu veux ajouter une fonctionnalité qui sera utilisée par toutes tes classes tu la code pas dans toutes tes classes
    Cette classe n'hérites d'aucune classe. Tu peux donc tout à fait faire une classe mère pour y mettre toutes les fonctionnalités dont tu as besoin.

    mais faire ça soit même est difficilement réalisable ça demande énormément de temps pour chaque classe
    C'est pour cela que je ne comprends pas les gens qui le font eux même. Les outils (quand ils valent le coup) sont là pour ça. C'est en tout cas mon avis.

    j'aimerai pas passer derrière en cas de bug
    C'est pourtant pas bien compliqué (probablement moins que de passer par des POCO, DTO, DAL et je ne sais quel acronyme )
    Et au moins cette classe est utilisable simplement, par exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Customer customer = Customer.LoadByName("Toto");
    customer.Name = "Titi";
    customer.Save(); // Appelle Validate donc ne sauvegarde pas d'instance non valide

  11. #31
    Modérateur

    Homme Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 722
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 722
    Points : 5 100
    Points
    5 100
    Par défaut
    @Kikuts
    Ce dont nous discutons peut aussi être décomposé en DLL.
    J'ai un projet bibliothéque DataTransfertObject dans lequel je défini les éléments de base et générique d'un DTO.
    Puis dans – mon (mes) applis - et - mon projet bibliothéque DAL générique - je référence cette DLL et fait un Imports DataTransfertObject.
    Ensuite mon appli défini : les classes DTO (dont elle a besoin) et la DAL appli qui connait la structure de la BDD.
    Et entre les couches circule le DTO (léger)
    Je ne l'ai appliqué que récemment que sur de petites applis. client serveur (c'est un bon début pour se faire la main, et apprendre)


    Citation Envoyé par Sankasssss Voir le message
    Concernant la duplication des méthodes, je ne pense pas que cela soit un problème. Elles contiennent bien 5 fois les mêmes méthodes mais qui n'agissent pas sur les mêmes données. C'est la même chose quand on fait une List(Of String), tous les objets ont bien toutes les méthodes de la class String.
    Bah, il me semble que même si la classe string procède ainsi, cela ne veut pas forcément dire que c’est l’idéal pour les perf. C’est simplement plus pratique pour l'utilisation courante que l'on en fait.
    On a bien des DataSet, des DataTable mais on cherche autre chose.

    Citation Envoyé par Sankasssss Voir le message
    Par contre si j'utilisais une collection de POCO, je pourrais avoir des propriétés calculées du genre Service, Recommande, etc et faire ma requête Linq directement sur ces propriétés sans connaitre la logique BD qui serait dans le POCO.
    C’est la que tu pourrais renvoyer une autre collection DTO différente de celle reçu dans le POCO. C'est une autre approche à étudier.
    Après une collection (POCO - DTO) permet peut être plus de manipulation des données.
    Mais une nouvelle collection de DTO ou une collection de POCO, il faudra reconstruire. Et c'est la qu'il y aura perte de performance.
    Il faut que je regarde ton code, je n'ai pas eu le temps.

    Citation Envoyé par Sankasssss Voir le message
    En voyant le terme BLL, j'ai recherché ça définition et je suis tombé sur ça.
    Donc maintenant je dirais que la collection de DTO que tu voulais mettre dans un POCO devrais à mon point de vue se trouver dans la BLL (même si moi je préférerais avoir une collection de POCO) qui contiendra la logique métier.
    Les POCO sont dans la BLL ; Business Logic Layer (couche de logique métier)
    Je sens qu'on se rapproche d'une meilleur compréhension, je vais encore approfondir les différents liens que je trouve au sujet d'application avec BLL, DAL et POCO.
    Bonne définition dans le lien.
    Oui, la logique métier est dans la BLL. Le POCO est donc dans la BLL. Et la collection de DTO circule entre les couches.

    La question est plus quelle est la meilleure approche (en ce qui nous concerne pour passer les données entre la BLL et l'UI)
    Collection (un poco – un dto) transmit à l’UI
    Ou (un POCO – collection de DTO) qui transmettra une nouvelle collection de DTO adapté à l’UI.

    A priori tu es en Winform, tes couches BLL et UI vont être sur le même poste, (reprend moi si je me suis trompé) à mon avis l'impact performance entre les deux collections à passer entre les couches sera peu perceptible. (faire un test sur un petit ensemble complexe)
    Il me semble que je verrai plus une approche ou la couche métier transmettra une nouvelle collection de DTO à l’UI , Ainsi si tu passes de Winform à WPF l’impact devrait être minime.
    Mais je n'ai pas assez de pratique à ce niveau.

    [Edit]Pour le développement en couche j'étais parti (Il y a déjà un moment) des tutos de serge Tahé (développement en couche avec C#) présent sur DVP. je chercherai lequels.[/Edit]

    Citation Envoyé par Sankasssss Voir le message
    Mais concernant la logique métier, tu la met où?
    Car c'est principalement là que nous nous embrouillons visiblement dans la discussion. rv26t crée un seul POCO qui contiendra une liste de DTO et qui aura des méthodes de traitement. Du coup j'ai l'impression que son objet ne doit plus être considéré comme un POCO mais autre.
    Moi je vois une collection de POCO contenant chacun la logique métier qui agirait sur son DTO (un POCO = un DTO), cette collection étant stockée dans un autre objet (de la couche BLL ??? ) qui elle contient des méthodes agissant sur la collection de POCO.
    L’un ou l’autre se trouve dans la BLL.

    Quel peut être le critère de choix entre ta conception et la mienne.
    La construction d’une liste de POCO ou d'un nouveau DTO, l’impact doit quasiment être le même. (on va avoir une perte de perf)
    Le transfert de la collection (DTO ou POCO) peut avoir un impact.
    Si tu es sous winform et que tes couches UI et BLL sont sur le même poste, l’un ou l’autre ne changera que peu de chose.
    Il me semble que si tu sépares entre un serveur applicatif (pour la bll) et poste utilisateur (UI) (je n'ai pas d'expérience à ce niveau, mais je commencais à regarder) ou évolue vers VB-ASP.NET cela peut avoir un impact. Sur ces derniers point, l'expérience d'autres membres pourrait nous éclairer.

    Et si je vois les choses d'une autre façon, c'est simplement pour que tu étudies les 2 possibilités. (la mienne n'est pas forcément la meilleure ou adapté à ton projet.)

    Citation Envoyé par meziantou Voir le message
    @Sankasssss : En quoi les DTO seraient plus rapide à faire transiter sur le réseau?
    Dans le cas ou le volume de données est important et fréquent. le DTO est très léger (c'est indiqué dans le tuto) mais cela ne s'applique que dans le cas ou la performance est importante. Donc dans peu de cas.
    Les POCO, c'est pour bien répartir les fonctionnalités. (responsabilité unique - voir tuto)
    DTO et POCO ne sont pas forcemment liés.

    Après, comme cela a déjà été dit, ça dépend de la taille des projets et de la répartition des accès.

    Il y a peu, dans un autre forum, quelqu'un demandait une piste car les serveurs étaient déplacés dans un autre pays, et les perf étaient en chute.
    Traductions d'articles :
    La mémoire en .NET - Qu'est-ce qui va où ?
    Architecture DAL de haute performance et DTO ; Version C# : Partie 1,Partie 2,Partie 3 — Version VB.NET : Partie 1,Partie 2,Partie 3
    N'hésitez pas à consulter la FAQ VB.NET, le cours complet de Philippe Lasserre et tous les cours, articles et tutoriels.

  12. #32
    Modérateur

    Homme Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 722
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 722
    Points : 5 100
    Points
    5 100
    Par défaut
    Citation Envoyé par Sankasssss Voir le message
    Moi j'aurais stocké le POCO dans la GUI pour pouvoir appeler les différentes méthodes de mon POCO dans celle-ci.
    Citation Envoyé par Sankasssss Voir le message
    Mais concernant la logique métier, tu la met où?
    En fait, ce qui me dérange avec ton principe, passer une collection de POCO à l’UI, c’est que tu passes de la logique métier dans ta couche UI.
    Traductions d'articles :
    La mémoire en .NET - Qu'est-ce qui va où ?
    Architecture DAL de haute performance et DTO ; Version C# : Partie 1,Partie 2,Partie 3 — Version VB.NET : Partie 1,Partie 2,Partie 3
    N'hésitez pas à consulter la FAQ VB.NET, le cours complet de Philippe Lasserre et tous les cours, articles et tutoriels.

  13. #33
    Membre émérite Avatar de meziantou
    Homme Profil pro
    Ingénieur R&D
    Inscrit en
    Avril 2010
    Messages
    1 223
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Canada

    Informations professionnelles :
    Activité : Ingénieur R&D
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2010
    Messages : 1 223
    Points : 2 439
    Points
    2 439
    Par défaut
    Dans le cas ou le volume de données est important et fréquent. le DTO est très léger (c'est indiqué dans le tuto) mais cela ne s'applique que dans le cas ou la performance est importante. Donc dans peu de cas.
    Sur le réseau, seul les données transitent. Que ton objet ait 10 méthodes ou 0, cela ne change pas. La différence peut cependant se trouver dans le cas où tu fais des traitements dans les setter. Ai-je tord? sinon je ne comprends donc toujours pas l'intérêt des DTO pour les performances.

    responsabilité unique
    J'aime bien ce principe car tout le monde comprend ce que ça veut dire mais il est très difficile de définir où s'arrête la limite de cette responsabilité unique.
    http://www.codinghorror.com/blog/200...one-thing.html

  14. #34
    Expert confirmé
    Avatar de Kropernic
    Homme Profil pro
    Analyste / Programmeur / DBA
    Inscrit en
    Juillet 2006
    Messages
    3 932
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Belgique

    Informations professionnelles :
    Activité : Analyste / Programmeur / DBA
    Secteur : Distribution

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 932
    Points : 4 239
    Points
    4 239
    Par défaut
    Citation Envoyé par rv26t Voir le message
    En fait, ce qui me dérange avec ton principe, passer une collection de POCO à l’UI, c’est que tu passes de la logique métier dans ta couche UI.
    Hello, (je continue à vous lire dans l'ombre)

    Je n'ai peut-être pas bien saisi le sens du verbe "passer" pour vous mais cela me semble normal...

    Pour que la couche GUI puisse consommer les méthodes métier de la couche BLL, il faut bien que, d'une manière ou d'une autre, elle (GUI) ait accès à un objet métier (POCO ou autre, peu importe).

    Qu'elle l'instancie directement ou qu'elle le reçoive, quelle différence ?
    Kropernic

  15. #35
    Modérateur

    Homme Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 722
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 722
    Points : 5 100
    Points
    5 100
    Par défaut
    Citation Envoyé par Kropernic Voir le message
    Hello, [SIZE=1]Pour que la couche GUI puisse consommer les méthodes métier de la couche BLL, il faut bien que, d'une manière ou d'une autre, elle (GUI) ait accès à un objet métier (POCO ou autre, peu importe).

    Qu'elle l'instancie directement ou qu'elle le reçoive, quelle différence ?
    Bien sur que l'UI va instancier les objets de la BLL. Seulement je voyais plus des méthodes qui renvoyaient des DTO. (c'est le principe des DTO)
    Sankasssss utilisera plus directement ses objets BLL, comme on le fait avec un DataTable, sauf qu'ils seront bien adapté à son besoin. C'est une bonne approche, je lui indique simplement l'autre possibilité.

    Moi aussi je creuse un peu, vu que je n'utilisais pas les POCO, et cette solution envisagé par Sankasssss semble plus pratique. Et le fait d'en discuter éclaircie les idées et fait progresser dans la compréhention.

    [Edit]Vu que je n’ai pas été très clair, je reformule ma pensée.
    On peut utiliser le même principe des DTO entre la couche UI et la couche BLL, comme on l’envisage avec les DTO entre la couche DAL et la couche BLL.
    J’espère avoir été plus clair. (désolé pour cette correction tardive)[/Edit]
    Traductions d'articles :
    La mémoire en .NET - Qu'est-ce qui va où ?
    Architecture DAL de haute performance et DTO ; Version C# : Partie 1,Partie 2,Partie 3 — Version VB.NET : Partie 1,Partie 2,Partie 3
    N'hésitez pas à consulter la FAQ VB.NET, le cours complet de Philippe Lasserre et tous les cours, articles et tutoriels.

  16. #36
    Membre éprouvé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2006
    Messages
    436
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2006
    Messages : 436
    Points : 963
    Points
    963
    Par défaut
    Citation Envoyé par Sankasssss Voir le message
    @Kropernic : Avant de me lancer la dedans il faudrait m'assurer que je comprenne bien la différence entre chaque type d'objet

    @Kikuts : Ok pour les intefaces, on reste effectivement dans la définition du DTO.
    Mais concernant la logique métier, tu la met où?
    Car c'est principalement là que nous nous embrouillons visiblement dans la discussion. rv26t crée un seul POCO qui contiendra une liste de DTO et qui aura des méthodes de traitement. Du coup j'ai l'impression que son objet ne doit plus être considéré comme un POCO mais autre.
    Moi je vois une collection de POCO contenant chacun la logique métier qui agirait sur son DTO (un POCO = un DTO), cette collection étant stockée dans un autre objet (de la couche BLL ??? ) qui elle contient des méthodes agissant sur la collection de POCO.

    P.S. : Si tu as des ressources traitant des bonnes pratiques on est preneur
    Je place toute ma logique dans une brique BLL (business logic layer) car le problème avec les POCOs, c'est que s'ils sont destinés à un type de projet (par exemple : les poco sont blindés de dataanotations, validations en tout genre etc or dans un projet unitaire qui se doit d'être performant, on se moque des raisepropertychanged et compagnie (chose super répandu dans les DTO) et bien le fait d'utiliser des DTO sera plus performant. De plus la remonté d'erreur dans les setter peut être différente d'un projet à l'autre (Silverlight / Windows phone / ASP.NET) on va utiliser des mécanismes différents (pas forcément mais possible)

    Je pace toute ma logique de validation dans une brique séparée qui s'occupe de valider des IObjets (ainsi je peux valider un model silverlight qui contient ses propres property nécessaire pour l'UI etc (property de type visibility par ex) mais je peux aussi valider un model utilisé dans mon site asp.net)

    Je n'ai pas de limite. Or, je trouve qu'avec les POCO, dans les projets qui utilisent différentes plateformes, on se limite, perd en perf et souplesse.

    Mais ce n'et que mon opinion. Je travaille sur un très gros projet. Si j'étais sur un petit projet simple, peut être n'utiliserais je pas les DTO et uniquement des POCO qui rempliraient le rôle de DTO en plus. Mais je garderai à l'esprit que ce serait le cirque si je voulais étendre le nombre de plateforme / cible.

    Que faites vous si vous travaillez avec des prestataires ?
    Ma logique métier est côté serveur dans un projet que je ne passerai pas à un prestataire. Si je travaille avec un externe, je préfère lui passer ma lib d'interfaces et ma lib dto et lui donner l'adresse de mon webservice plutôt qu'accès à tout.
    "S'adapter, c'est vaincre" - Cellendhyll de Cortavar

  17. #37
    Expert confirmé

    Homme Profil pro
    Développeur .NET
    Inscrit en
    Novembre 2010
    Messages
    2 065
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Novembre 2010
    Messages : 2 065
    Points : 4 229
    Points
    4 229
    Par défaut
    Dans ton cas Meziantou ta classe ne respecte pas le principe de responsabilité unique c'est un peu une classe fourre-tout, tu as ton objet Customer et à l'intérieur toutes les méthodes d'accès aux données, c'est à la BLL de te renvoyer ton Customer ou ta liste de Customer, qui elle même appel ta dal qui s'occupe de faire les requêtes en BDD, comme ça tu peux faire des modifications dans ta DAL sans impacter ta BLL et ton objet Customer.

    Un très bon exemple d'archi Multicouche avec l'utilisation d'un DTO: http://immobilis.developpez.com/arti...ouche-asp-net/

  18. #38
    Membre émérite Avatar de meziantou
    Homme Profil pro
    Ingénieur R&D
    Inscrit en
    Avril 2010
    Messages
    1 223
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Canada

    Informations professionnelles :
    Activité : Ingénieur R&D
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2010
    Messages : 1 223
    Points : 2 439
    Points
    2 439
    Par défaut
    Citation Envoyé par youtpout978 Voir le message
    Dans ton cas Meziantou ta classe ne respecte pas le principe de responsabilité unique c'est un peu une classe fourre-tout, tu as ton objet Customer et à l'intérieur toutes les méthodes d'accès aux données, c'est à la BLL de te renvoyer ton Customer ou ta liste de Customer, qui elle même appel ta dal qui s'occupe de faire les requêtes en BDD, comme ça tu peux faire des modifications dans ta DAL sans impacter ta BLL et ton objet Customer
    Pour ma part je ne suis pas d'accord avec ce que tu dis. Je peux très bien modifier ma couche de persistence sans modifier mon code => ADO.NET est là pour ça !

    Si tu regardes de plus près, ma méthode Load indique juste d'appeler une procédure stockée avec les paramètres requis. Cette classe n'a en aucun cas la responsabilité de faire une requete SQL ou je ne sais quoi. D'ailleur rien ne dis que derrière il y a vraiment une base de données et une proc stock. Cela dépend du provider que tu utilises. Si demain je souhaites passer de SQL Server à un fichier CSV, il suffit que je code un provider ADO.NET qui renvoit les données qu'il faut lorsque je demande de charger mes Customers avec les paramètres voulus.

    Un autre exemple, si je souhaite utiliser SQL CE (qui ne dispose pas de procédure stocké), il suffit de faire un wrapper autour du provider ADO.NET de SQL CE qui remplacera les appels aux procédures stockées par les requêtes SQL correspondantes.

    Pour moi la DAL est gérer par ADO.NET (et le provider utilisé). Je ne souhaite donc pas ajouter une couche qui est à mon sens inutile puisqu'elle existe déjà.

  19. #39
    Expert confirmé

    Homme Profil pro
    Développeur .NET
    Inscrit en
    Novembre 2010
    Messages
    2 065
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Novembre 2010
    Messages : 2 065
    Points : 4 229
    Points
    4 229
    Par défaut
    Citation Envoyé par meziantou Voir le message
    Pour ma part je ne suis pas d'accord avec ce que tu dis. Je peux très bien modifier ma couche de persistence sans modifier mon code => ADO.NET est là pour ça !

    Si tu regardes de plus près, ma méthode Load indique juste d'appeler une procédure stockée avec les paramètres requis. Cette classe n'a en aucun cas la responsabilité de faire une requete SQL ou je ne sais quoi. D'ailleur rien ne dis que derrière il y a vraiment une base de données et une proc stock. Cela dépend du provider que tu utilises. Si demain je souhaites passer de SQL Server à un fichier CSV, il suffit que je code un provider ADO.NET qui renvoit les données qu'il faut lorsque je demande de charger mes Customers avec les paramètres voulus.

    Un autre exemple, si je souhaite utiliser SQL CE (qui ne dispose pas de procédure stocké), il suffit de faire un wrapper autour du provider ADO.NET de SQL CE qui remplacera les appels aux procédures stockées par les requêtes SQL correspondantes.

    Pour moi la DAL est gérer par ADO.NET (et le provider utilisé). Je ne souhaite donc pas ajouter une couche qui est à mon sens inutile puisqu'elle existe déjà.
    Et si tu veux passer par un webservice et donc transmettre ton Customer par l'intermédiaire de ce webservice ?
    Je pense aussi que tu cernes mal les définitions de chaque couche.
    Et il existe tout un tas de pattern que tu ne pourras pas mettre en place avec ton architecture comme l'IOC.

  20. #40
    Membre émérite Avatar de meziantou
    Homme Profil pro
    Ingénieur R&D
    Inscrit en
    Avril 2010
    Messages
    1 223
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Canada

    Informations professionnelles :
    Activité : Ingénieur R&D
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2010
    Messages : 1 223
    Points : 2 439
    Points
    2 439
    Par défaut
    Et si tu veux passer par un webservice et donc transmettre ton Customer par l'intermédiaire de ce webservice ?
    Le service WCF est très facile à implémenter :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    [System.ServiceModel.ServiceContractAttribute()]
    public partial interface ICustomerService
    {
     
        [System.ServiceModel.OperationContractAttribute()]
        string Validate(Model1.Customer customer, string culture);
     
        [System.ServiceModel.OperationContractAttribute()]
        bool Delete(Model1.Customer customer);
     
        [System.ServiceModel.OperationContractAttribute()]
        Model1.Customer Load(int id);
     
        [System.ServiceModel.OperationContractAttribute()]
        Model1.Customer LoadById(int id);
     
        [System.ServiceModel.OperationContractAttribute()]
        bool Save(Model1.Customer customer);
     
        [System.ServiceModel.OperationContractAttribute()]
        bool SaveByRef(ref Model1.Customer customer);
     
        [System.ServiceModel.OperationContractAttribute()]
        Model1.Customer LoadByEntityKey(string key);
     
        [System.ServiceModel.OperationContractAttribute()]
        bool DeleteByKey(int id);
     
        [System.ServiceModel.OperationContractAttribute()]
        void SaveAll(Model1.CustomerCollection customerCollection);
     
        [System.ServiceModel.OperationContractAttribute()]
        Model1.CustomerCollection PageLoadAll(int pageIndex, int pageSize, CodeFluent.Runtime.PageOptions pageOptions);
     
        [System.ServiceModel.OperationContractAttribute()]
        Model1.CustomerCollection LoadAll();
    }
     
    public partial class CustomerService : Model1.Services.ICustomerService
    {
     
        public virtual string Validate(Model1.Customer customer, string culture)
        {
            System.Globalization.CultureInfo cultureInfo = ConvertUtilities.ToCultureInfo(culture);
            Model1.Customer customer1 = customer;
            if ((customer1 == null))
            {
                return default(string);
            }
            return customer1.Validate(cultureInfo);
        }
     
        public virtual bool Delete(Model1.Customer customer)
        {
            Model1.Customer customer1 = customer;
            if ((customer1 == null))
            {
                return CodeFluentPersistence.DefaultBooleanValue;
            }
            return customer1.Delete();
        }
     
        public virtual Model1.Customer Load(int id)
        {
            return Model1.Customer.Load(id);
        }
     
        public virtual Model1.Customer LoadById(int id)
        {
            return Model1.Customer.LoadById(id);
        }
     
        public virtual bool Save(Model1.Customer customer)
        {
            Model1.Customer customer1 = customer;
            if ((customer1 == null))
            {
                return CodeFluentPersistence.DefaultBooleanValue;
            }
            return customer1.Save();
        }
     
        public virtual bool SaveByRef(ref Model1.Customer customer)
        {
            Model1.Customer customer1 = customer;
            if ((customer1 == null))
            {
                return CodeFluentPersistence.DefaultBooleanValue;
            }
            bool ret = customer1.Save();
            customer = customer1;
            return ret;
        }
     
        public virtual Model1.Customer LoadByEntityKey(string key)
        {
            return Model1.Customer.LoadByEntityKey(key);
        }
     
        public virtual bool DeleteByKey(int id)
        {
            Model1.Customer customer = Model1.Customer.Load(id);
            if ((customer == null))
            {
                return CodeFluentPersistence.DefaultBooleanValue;
            }
            return customer.Delete();
        }
     
        public virtual void SaveAll(Model1.CustomerCollection customerCollection)
        {
            Model1.CustomerCollection customerCollection1 = customerCollection;
            customerCollection1.SaveAll();
        }
     
        public virtual Model1.CustomerCollection PageLoadAll(int pageIndex, int pageSize, CodeFluent.Runtime.PageOptions pageOptions)
        {
            return Model1.CustomerCollection.PageLoadAll(pageIndex, pageSize, pageOptions);
        }
     
        public virtual Model1.CustomerCollection LoadAll()
        {
            return Model1.CustomerCollection.LoadAll();
        }
    }
    Je pense aussi que tu cernes mal les définitions de chaque couche.
    C'est possible mais pour moi j'ai une DAL (ADO.NET) et une BLL (avec une classe par entité du même genre que celle présenté). Pourrais-tu m'expliquer plus en détails le problème?

    Et il existe tout un tas de pattern que tu ne pourras pas mettre en place avec ton architecture comme l'IOC.
    Quel est l'intérêt de l'IOC pour mon cas ? et quels autres patterns utiles ne puis-je pas implémenter ?

Discussions similaires

  1. Domain-Driven Design: DAL, DAO, DTO, POCO, Repo et contextes
    Par Finality dans le forum Entity Framework
    Réponses: 1
    Dernier message: 19/07/2012, 12h10
  2. Réponses: 9
    Dernier message: 23/04/2012, 16h36
  3. Dessiner dans une couche d'un JLayeredPane ?
    Par eikichi972 dans le forum 2D
    Réponses: 2
    Dernier message: 05/12/2008, 04h05
  4. Quel code dans la couche services ?
    Par speedster dans le forum Spring
    Réponses: 9
    Dernier message: 24/04/2007, 10h01
  5. Exploitation de GregorianCalendar, Date dans une couche DAO
    Par wdionysos dans le forum Collection et Stream
    Réponses: 8
    Dernier message: 10/01/2006, 18h04

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo