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 :

De l'utilisation des DTO


Sujet :

VB.NET

  1. #1
    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 De l'utilisation des DTO
    Hello,

    Je reviens une fois de plus sur un sujet qui me tiens à coeur de comprendre, les DTO et leur utilisation.

    Je ne link plus les différents articles qui existe mais pour rappel, il y a les traductions de rvt26 des article de Rudy Lavocara (j'écorche sûrement son nom) ainsi qu'un article de skalp (cela devrait suffir pour que les curieux qui ne les auraient pas encore lu puisse les trouver).

    Il s'agit de travailler sur un projet multi-couche organisé comme suit :
    • une couche DTO qui contient les objets de bases
    • une couche DAL qui communique avec la DB (qui fait référence la couche DTO)
    • une couche BLL qui communique avec la DAL au moyen de DTO (qui fait référence à DAL et DTO)
    • une couche GUI qui communique avec la BLL au moyen de DTO (qui fait référence à BLL et DTO)


    Voilà pour l'architecture...

    Mon souci se situe dans la couche BLL.

    Dans un précédent projet, je structurais une classe de la couche BLL comme suit :
    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
     
    Public Class BllClass
        Public Property DTO as DtoClass
     
        Public Sub New(dto as DtoClass)
            DTO = dto
        End Sub
     
        Public Shared Function GetFromDB(id as Integer) as BllClass
            Return New BllClass(DAL.DalClass.GetOne(id))
        End Function
     
        Public Sub Save()
            DAL.DalClass.Save(DTO)
        End Sub
    End Class
    Public Class BllClasses
        Public Property DTO as List(Of DtoClass)
     
        Public Sub New (dtos as List(Of DtoClass)
            DTO = dtos
        End Sub
     
        Public Shared Function GetFromDb(param as object) as BllClasses
            Return New BllClasses(DAL.DalClass.GetList(param))
        End Function
     
        Public Function GetOne(id as Integer) as BllClass
            Dim dto as DtoClass = (For d as DtoClass in Me.DTO
                                           Where d.Id = id
                                           Select d).FirstOrDefault
            Return New BllClass(dto)
        End Function
     
        Public Sub Save()
            For Each d as DtoClass in DTO
                DAL.DalClass.Save(d)
            End For
        End Sub
    End Class
    Avantage : Pas de duplication de code au niveau des propriétés des DTO. La couche GUI peut passer le DTO de l'objet BLL directement à un DataGridView par exemple.
    Désavantage : 2 classes au lieu d'une. Une pour gérer un seul DTO et une pour gérer une liste.

    J'ai brièvement réfléchi à comment éviter d'avoir cette duplication de classe et je suis arrivé à ceci :
    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
     
    Public Class BllClass
        Public Property Id as Integer
        Public Property P1 as Integer
        Public Property P2 as String
     
        Public Sub New(id as Integer, p1 as Integer, p2 as String)
            Me.Id = id
            Me.P1 = p1
            Me.P2 = p2
        End Sub
     
        Public Shared Function GetOneFromDb(id as Integer) as BllClass
            Dim dto as DtoClass = DAL.DalClass.GetOne(id)
            Return New BllClass(dto.Id, dto.P1, dto.P2)
        End Function
     
        Public Shared Function GetListFromDb(param as Object) as List(Of BllClass)
            Dim result as New List(Of BllClass)
            Dim dtos as List(Of DtoClass) = DAL.DalClass.GetList(param)
            For Each d as DtoClass in dtos
                result.Add(New BllClass(d.Id, d.P1, d.P2)
            End For
            Return result
        End Function
    End Class
    N.B. : Il manque un test ou l'autre mais l'idée est là...

    Avantage : Une seule classe au lieu de deux.
    Désavantage : Plus de code à écrire (il faut reproduire les propriétés du DTO dans BLL)

    Mais avec ces solutions en 1 seule class, j'ai l'impression de perdre la philosophie des DTO's dans le sens que la couche GUI n'a même plus besoin de référencer la couche DTO puisqu'elle ne travaillera qu'avec les objets de la couche BLL et n'aura même pas conscience des DTO's sous-jacents. Pour afficher une liste dans un DataGridView, elle donnera une liste de BllClass et non pas une liste DtoClass.

    Etant donné que je débute encore avec tout cela, je me tourne vers vous pour savoir quelle solution il faut préférer car cela a probablement des implications auxquelles je ne pense pas. Et je suis malheureusement sur un projet où je ne peux pas me permettre de perdre du temps à revenir en arrière à cause d'un souci auquel je n'aurais pas pensé causé par modélisation de la couche BLL... :-/

    Vous faites comment vous ?
    Kropernic

  2. #2
    Modérateur
    Avatar de DotNetMatt
    Homme Profil pro
    CTO
    Inscrit en
    Février 2010
    Messages
    3 611
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : CTO
    Secteur : Finance

    Informations forums :
    Inscription : Février 2010
    Messages : 3 611
    Points : 9 743
    Points
    9 743
    Billets dans le blog
    3
    Par défaut
    Dans l'absolu, tu dois rester sur le premier cas : le DTO est là pour voyager de couche en couche au travers de l'application, c'est son job. Ca simplifie également la maintenance car on regroupe à un endroit.

    Citation Envoyé par Kropernic
    Désavantage : 2 classes au lieu d'une. Une pour gérer un seul DTO et une pour gérer une liste.
    Effectivement ce n'est pas top, sauf s'il y a des besoins particuliers, comme par exemple pouvoir implémenter sa propre méthode Add pour ajouter des éléments à la liste et faire des vérifications en même temps... Mais rien ne t'empêche de caster ton BllClasses en tant que List(Of A) (son type de base) à la sortie de la BLL... Donc ce n'est pas forcément bloquant non plus.

    Si ce post est lié à ton précédent post et au problème de faire une boucle, je pense que quoi qu'il advienne, tu vas devoir boucler : soit tu le fais dans ta classe BllClasses, soit tu le fais directement dans la DAL. Peu importe, à toi de choisir. Le plus simple de mon point de vue, c'est de le faire dans la DAL comme ça tu peux trimbaler des IEnumerables (ou autre) de DTO dans ton appli.
    Less Is More
    Pensez à utiliser les boutons , et les balises code
    Desole pour l'absence d'accents, clavier US oblige
    Celui qui pense qu'un professionnel coute cher n'a aucune idee de ce que peut lui couter un incompetent.

  3. #3
    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
    Cette discussion n'est pas du tout liée à une autre ;-)

    Je vais donc rester dans la première situation. C'est bien ce qu'il me semblait. C'est plus proche de la philosophie des DTO et au final, c'est plus simple à gérer dans la couche GUI. Juste un type singulier et un pluriel.

    Si d'autres personnes ont des avis et/ou méthodes d'utilisation différentes, ils sont plus que les bienvenus pour les exprimer.
    Kropernic

  4. #4
    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
    Question corolaire : Dans la classe au plusieurs, est-ce judicieux d'utiliser une List(Of DtoClass) ?

    Plus précisément, bien souvent à l'utilisation, celle liste va être passée en datasource d'un objet graphique. Or, il est recommandé d'utiliser des BindingList plutôt que des listes simples. On peut faire le cast de List vers BindingList sans souci ou bien il faut créé la bindinglist et ajouter les éléments un à un ?
    Kropernic

  5. #5
    Modérateur
    Avatar de DotNetMatt
    Homme Profil pro
    CTO
    Inscrit en
    Février 2010
    Messages
    3 611
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : CTO
    Secteur : Finance

    Informations forums :
    Inscription : Février 2010
    Messages : 3 611
    Points : 9 743
    Points
    9 743
    Billets dans le blog
    3
    Par défaut
    L'intérêt des BindingList c'est surtout de faciliter la gestion du binding dans les deux sens (two-ways binding), notamment grâce à l'évènement ListChanged (porté par l'interface IBindingList) qui permet de détecter les changements et de rafraichir l'UI. Il faut voir ça comme un "wrapper" qui encapsule une liste typée... Donc ça restreint déjà les cas où il est réellement intéressant de les utiliser

    Sinon pour passer d'une BindingList à une List, il suffit d'utiliser la méthode .ToList() de la BindingList.
    Less Is More
    Pensez à utiliser les boutons , et les balises code
    Desole pour l'absence d'accents, clavier US oblige
    Celui qui pense qu'un professionnel coute cher n'a aucune idee de ce que peut lui couter un incompetent.

  6. #6
    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 DotNetMatt Voir le message
    L'intérêt des BindingList c'est surtout de faciliter la gestion du binding dans les deux sens (two-ways binding), notamment grâce à l'évènement ListChanged (porté par l'interface IBindingList) qui permet de détecter les changements et de rafraichir l'UI. Il faut voir ça comme un "wrapper" qui encapsule une liste typée... Donc ça restreint déjà les cas où il est réellement intéressant de les utiliser

    Sinon pour passer d'une BindingList à une List, il suffit d'utiliser la méthode .ToList() de la BindingList.
    BindingList permet surtout d'éviter des trucs débiles comme celle-ci XD
    Kropernic

  7. #7
    Modérateur
    Avatar de DotNetMatt
    Homme Profil pro
    CTO
    Inscrit en
    Février 2010
    Messages
    3 611
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : CTO
    Secteur : Finance

    Informations forums :
    Inscription : Février 2010
    Messages : 3 611
    Points : 9 743
    Points
    9 743
    Billets dans le blog
    3
    Par défaut
    Sur la discussion que tu mentionnes, si mes souvenirs sont bons, c'est parce que tu modifiais la collection qui était bindée avec la méthode RefreshTeams (ou quelque chose comme ça). Donc là oui ça fait sens, mais sinon ce n'est pas la peine, je n'en ai quasiment jamais utilisé... De simples listes suffisent en règle générale, même des IEnumerables.
    Less Is More
    Pensez à utiliser les boutons , et les balises code
    Desole pour l'absence d'accents, clavier US oblige
    Celui qui pense qu'un professionnel coute cher n'a aucune idee de ce que peut lui couter un incompetent.

  8. #8
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2012
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2012
    Messages : 640
    Points : 372
    Points
    372
    Par défaut
    Bonsoir à tous,
    Je me suis aussi posé ce genre de question et j'apporte ma modeste contribution.
    Comme je suis un autodidacte de la programmation je suis conscient de faire beaucoup d’erreurs. Je vous montre juste mon code si jamais il y avait des bonnes idées à piocher dedans mais je précise que j'ai plus de conseil à prendre qu'a en donner. D'ailleurs ça serait bien si il existait des modèles tout prêt de Bll, Dto, etc... qui pourrait convenir à la plupart des cas mais je ne trouve que des bouts de codes et pas vraiment d'exemple complet avec la validation, les règles métier etc....
    J'avais commencé à implémenter ce bout de code en vue d'améliorer une architecture existante mais (bien que ça fonctionne) étant donné que je ne suis pas sûre de moi je ne l'ai utilisé que sur une petite partie de mon programme pour tester la chose.
    Mes DTO héritent de DTOBase, ca alourdi un peu le DTO mais ainsi il peut par exemple être attaché à un DataRow et se sauvegarder lui-même (bonne idée ou pas ? je n'en sais rien mais j'ai trouvé ça pratique).
    Autre idée : mon DTO Notifie ses changements à la BLL qui lui est rattaché, tous les calculs sont ainsi déportés dans la BLL mais c'est son rôle je crois. De ce fait on peut indifféremment travailler directement sur une instance de DTO ou travailler avec la BLL.

    PS: Désolé si il y a beaucoup de lignes commentées, je n'ai pas nettoyé le code.

    DTOBase:
    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
    Imports System.Reflection
    Imports System.ComponentModel
     
    #Region "Custom Atributs"
    <AttributeUsage(AttributeTargets.All)>
    Public Class DataInfoAttribute
        Inherits System.Attribute
        ' Toutes les classes d'attribut héritent de System.Attribute
        Sub New(ByVal FieldName As String)
            Me.FieldName = FieldName
        End Sub
        Private _FielName As String
        Public Property FieldName() As String
            Get
                Return _FielName
            End Get
            Set(ByVal value As String)
                _FielName = value
            End Set
        End Property
        Private _dataType As Type
        Public Property DataType() As Type
            Get
                Return _dataType
            End Get
            Set(ByVal value As Type)
                _dataType = value
            End Set
        End Property
     
        Private _FielLenght As Integer
        Public Property FiedLenght() As Integer
            Get
                Return _FielLenght
            End Get
            Set(ByVal value As Integer)
                _FielLenght = value
            End Set
        End Property
    End Class
    #End Region
     
    #Region "DataEventArgs"
    Public Class DataEventArgs
        Public Sub New(ByVal PropertyName As String, ByVal OriginalValue As Object, ByVal ProposedValue As Object)
            Me.PropertyName = PropertyName
            Me.OriginalValue = OriginalValue
            Me.ProposedValue = ProposedValue
     
        End Sub
        Private _PropertyName As String
        Public Property PropertyName() As String
            Get
                Return _PropertyName
            End Get
            Set(ByVal value As String)
                _PropertyName = value
            End Set
        End Property
        Private _OriginaleValue As Object
        Public Property OriginalValue() As Object
            Get
                Return _OriginaleValue
            End Get
            Set(ByVal value As Object)
                _OriginaleValue = value
            End Set
        End Property
        Private _Proposedvalue As Object
        Public Property ProposedValue() As Object
            Get
                Return _Proposedvalue
            End Get
            Set(ByVal value As Object)
                _Proposedvalue = value
            End Set
        End Property
     
    End Class
     
    Public Class DataEventArgspropertyChanging
        Inherits DataEventArgs
        Public Sub New(ByVal PropertyName As String, ByVal OriginalValue As Object, ByVal ProposedValue As Object)
            MyBase.New(PropertyName, OriginalValue, ProposedValue)
            Me.Cancel = False
            Me.ErrorMessage = String.Empty
        End Sub
        Private _Cancel As Boolean
        Public Property Cancel() As Boolean
            Get
                Return _Cancel
            End Get
            Set(ByVal value As Boolean)
                _Cancel = value
            End Set
        End Property
        Private _ErrorMessage As String
        Public Property ErrorMessage() As String
            Get
                Return _ErrorMessage
            End Get
            Set(ByVal value As String)
                _ErrorMessage = value
            End Set
        End Property
    End Class
    #End Region
     
    Public MustInherit Class DTOBase
        Implements INotifyPropertyChanged
     
        Public Event PropertyChanged As PropertyChangedEventHandler _
            Implements INotifyPropertyChanged.PropertyChanged
     
     
        Event DataPropertyChanging(ByVal Sender As Object, ByVal e As DataEventArgspropertyChanging)
        Event DataPropertyChanged(ByVal Sender As Object, ByVal e As DataEventArgs)
        Dim DataEventArgsPropertyChanging As DataEventArgspropertyChanging
        'Dim _ErrorMessage As String
        Dim LockPropertyChanging As Boolean
        Protected Function NotifyPropertyChanging(ByVal PropertyName As String, ByVal OriginalValue As Object, ByVal ProposedValue As Object) As Boolean
            If _IsInizializingComponent = True Or LockPropertyChanging = True Or LockPropertyChanged = True Then
                'If _IsInizializingComponent = True Or LockPropertyChanging = True Then
                Return Nothing
                Exit Function
            End If
     
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName))
     
            LockPropertyChanging = True
            DataEventArgsPropertyChanging = New DataEventArgspropertyChanging(PropertyName, OriginalValue, ProposedValue)
            RaiseEvent DataPropertyChanging(Me, DataEventArgsPropertyChanging)
            LockPropertyChanging = False
            If DataEventArgsPropertyChanging.Cancel = True Then
                ' Modification Annulée
                '_ErrorMessage = DataEventArg.ErrorMessage
                'DataEventArg = Nothing
                Return True
                'Return OriginalValue
            Else
                ' Modification acceptée
                '_ErrorMessage = ""
                ' DataEventArg = Nothing
                'Dim ColName As String = GetAttributsByProp(PropertyName)
                'Save(ColName, ProposedValue)
                Return False
                'Return ProposedValue
            End If
        End Function
        Dim LockPropertyChanged As Boolean
        Protected Sub NotifyPropertyChanged(ByVal PropertyName As String, ByVal Value As Object)
            If _IsInizializingComponent = True Or LockPropertyChanging = True Or LockPropertyChanged = True Then Exit Sub
            'If _IsInizializingComponent = True Or LockPropertyChanging = True Then Exit Sub
     
            LockPropertyChanged = True
            Dim DataEventArg = New DataEventArgs(PropertyName, Value, Value)
            RaiseEvent DataPropertyChanged(Me, DataEventArg)
            LockPropertyChanging = False
            LockPropertyChanged = False
        End Sub
     
        Protected _IsInizializingComponent As Boolean
        Private _DicoAttributes As New Dictionary(Of String, DataInfoAttribute)
        Private _DicoProperties As New Dictionary(Of String, PropertyInfo)
        Private _DicoAttToProperties As New Dictionary(Of String, String)
        Private _DicoPropToAttributs As New Dictionary(Of String, String)
        Public Sub New()
            Call LoadListesAttributesAndProperties()
            _IsNew = True
        End Sub
        Public Sub New(ByVal RowView As DataRowView)
            Me.New()
            Me.RowViewSource = RowView
            Call SetProperties(RowView)
            _IsNew = False
        End Sub
     
        Private _IsNew As Boolean
        Public ReadOnly Property IsNew() As Boolean
            Get
                Return _IsNew
            End Get
            'Set(ByVal value As Boolean)
            '    _IsNew = value
            'End Set
        End Property
        Private _RowViewSource As DataRowView
        Public Property RowViewSource() As DataRowView
            Get
                Return _RowViewSource
            End Get
            Set(ByVal value As DataRowView)
                _RowViewSource = value
                _IsNew = False
            End Set
        End Property
     
        Public ReadOnly Property Attributs() As Dictionary(Of String, DataInfoAttribute)
            Get
                Return _DicoAttributes
            End Get
            'Set(ByVal value As Dictionary(Of String, DataInfoAttribute))
            '    _DicoPropToAttributs = value
            'End Set
        End Property
        'Private _DicoPropToAttributs As Dictionary(Of String, DataInfoAttribute)
        Public ReadOnly Property Properties() As Dictionary(Of String, PropertyInfo)
            Get
                Return _DicoProperties
            End Get
            'Set(ByVal value As Dictionary(Of String, DataInfoAttribute))
            '    _DicoPropToAttributs = value
            'End Set
        End Property
        Public Function GetAttributsByProp(ByVal PropertyName As String) As String
            If Not _DicoPropToAttributs.ContainsKey(PropertyName) Then
                Throw New Exception(String.Format("Le dictionnaire des Attributs ne contient pas la propriété '{0}' du DTO '{1}'", PropertyName, Me.ToString))
                Exit Function
            End If
            'Dim KeyAtt As String = _DicoPropToAttributs(PropertyName)
            'Return _DicoAttributes(KeyAtt)
            Return _DicoPropToAttributs(PropertyName)
        End Function
     
        Public Sub Save()
            If Me.IsNew = True Or Me.RowViewSource Is Nothing Then
                Throw New Exception(String.Format("le DTO '{0}' est détaché ou la propriété 'RowSource' n'a pas été assignée", Me.ToString))
                Exit Sub
            End If
     
            SaveProperties(Me.RowViewSource)
     
        End Sub
        Public Sub Save(ByVal ColChangingName As String, Optional ByVal SetRequestToDBNull As Boolean = False)
            If Me.IsNew = True Or Me.RowViewSource Is Nothing Then
                Throw New Exception(String.Format("le DTO '{0}' est détaché ou la propriété 'RowSource' n'a pas été assignée", Me.ToString))
                Exit Sub
            End If
     
            Dim propriété As PropertyInfo
     
            If Not _DicoAttToProperties.ContainsKey(ColChangingName) Then
                Throw New Exception(String.Format("Le dictionnaire des propriétés ne contient pas la clé du champ '{0}' de l'objet métier '{1}'", ColChangingName, Me.ToString))
                Exit Sub
            Else
                Dim Keyprop As String = _DicoAttToProperties(ColChangingName)
                propriété = _DicoProperties(Keyprop)
                Dim value As Object = propriété.GetValue(Me, Nothing)
     
                SaveProperty(Me.RowViewSource, ColChangingName, value, SetRequestToDBNull)
     
            End If
        End Sub
        Private Sub Save(ByVal ColChangingName As String, ByVal Value As Object, Optional ByVal SetRequestToDBNull As Boolean = False)
            If Me.IsNew = True Or Me.RowViewSource Is Nothing Then
                Throw New Exception(String.Format("le DTO '{0}' est détaché ou la propriété 'RowSource' n'a pas été assignée", Me.ToString))
                Exit Sub
            End If
     
            SaveProperty(Me.RowViewSource, ColChangingName, Value, SetRequestToDBNull)
     
        End Sub
        Private Sub LoadListesAttributesAndProperties()
            Dim attributs() As Object
            Dim att As DataInfoAttribute
            'Dim methode As System.Reflection.MethodInfo
            Dim propriété As System.Reflection.PropertyInfo
            Dim attType As Type = GetType(DataInfoAttribute)
            'Dim classType As Type = GetType(DTOTache)
            Dim classType As Type = Me.GetType
     
            'Debug.WriteLine("Ordinal = " & Col.Ordinal.ToString & " - ColumnName = " & Col.ColumnName.ToString & " - Type = " & Col.DataType.ToString & " - Value = " & Value.ToString)
            For Each propriété In classType.GetProperties
                Dim keyProp As String = propriété.Name
                attributs = Attribute.GetCustomAttributes(propriété, attType)
                If attributs.Length > 0 Then
                    att = DirectCast(attributs(0), DataInfoAttribute)
                    Dim keyAtt As String = att.FieldName
                    If Not _DicoAttributes.ContainsKey(keyAtt) Then
                        _DicoAttributes.Add(keyAtt, att)
                        _DicoAttToProperties.Add(keyAtt, keyProp)
                        _DicoPropToAttributs.Add(keyProp, keyAtt)
                    Else
                        Throw New Exception(String.Format("L'attribut '{0}' existe déja dans le DTO '{1}'", keyAtt, Me.ToString))
                        Exit Sub
                    End If
                    'Debug.WriteLine("Propriété = " & propriété.Name)
                    'Debug.WriteLine("Field Name = " & att.FieldName)
                    'Debug.WriteLine("Field Lenght = " & att.FiedLenght)
                End If
     
                If Not _DicoProperties.ContainsKey(keyProp) Then
                    _DicoProperties.Add(keyProp, propriété)
                End If
            Next
        End Sub
     
        Private Sub SetProperties(ByVal RowView As DataRowView)
            _IsInizializingComponent = True
            Dim Row As DataRow = RowView.Row
     
            For Each Col As DataColumn In Row.Table.Columns
                Dim Value As Object = GetTrueValue(Row, Col)
                'Debug.WriteLine("Ordinal = " & Col.Ordinal.ToString & " - ColumnName = " & Col.ColumnName.ToString & " - Type = " & Col.DataType.ToString & " - Value = " & Value.ToString)
                Dim ColName As String = Col.ColumnName
                If _DicoAttToProperties.ContainsKey(ColName) Then
                    'Dim att As DataInfoAttribute = _DicoAttributes(KeyAtt)
                    'att.FiedLenght = Col.MaxLength
                    _DicoAttributes(ColName).FiedLenght = Col.MaxLength
                    _DicoAttributes(ColName).DataType = Col.DataType
                    Dim Keyprop As String = _DicoAttToProperties(ColName)
                    If _DicoProperties.ContainsKey(Keyprop) Then
                        Dim prop As PropertyInfo = _DicoProperties(Keyprop)
                        prop.SetValue(Me, Value, Nothing)
                    Else
                        Throw New Exception(String.Format("Le champ '{0}' du DataRow n'a pas pu initialiser la propriété correspondante du DTO '{1}'", Col.ColumnName, Me.ToString))
                        Exit Sub
                    End If
                Else
                    Throw New Exception(String.Format("Le champ '{0}' du DataRow n'a pas pu initialiser l'attribut correspondant du DTO '{1}'", Col.ColumnName, Me.ToString))
                    Exit Sub
                End If
            Next
            _IsInizializingComponent = False
        End Sub
        'Private IsSettingProperty As Boolean
        Public Function SetProperty(ByVal ColName As String, ByVal Value As Object) As DataEventArgspropertyChanging
            'IsSettingProperty = True
            Dim Keyprop As String = _DicoAttToProperties(ColName)
            If _DicoProperties.ContainsKey(Keyprop) Then
                Dim prop As PropertyInfo = _DicoProperties(Keyprop)
                prop.SetValue(Me, Value, Nothing)
            Else
                Throw New Exception(String.Format("Le champ '{0}' du DataRow n'a pas pu initialiser la propriété correspondante du DTO '{1}'", ColName, Me.ToString))
                Exit Function
            End If
            'IsSettingProperty = False
            Return DataEventArgsPropertyChanging
        End Function
        'Private Function GetPropertieInfoByDataInfoAttributName(ByVal DataInfoAttribut As DataInfoAttribute) As PropertyInfo
     
        '    Return GetPropertieInfoByDataInfoAttributName
        'End Function
        'Private Function GetDataAttributsByPropertyName(ByVal PropertyInfo As PropertyInfo) As DataInfoAttribute
     
        'End Function
        Private Sub SaveProperties(ByVal RowView As DataRowView)
            Dim Row As DataRow = RowView.Row
            Dim attributs() As Object
            Dim att As DataInfoAttribute
            'Dim methode As System.Reflection.MethodInfo
            Dim propriété As PropertyInfo
            Dim attType As Type = GetType(DataInfoAttribute)
            'Dim classType As Type = GetType(DTOTache)
            Dim classType As Type = Me.GetType
     
            For Each propriété In classType.GetProperties
                Dim MatchField As Boolean = False
                Dim Value As Object = propriété.GetValue(Me, Nothing)
                attributs = Attribute.GetCustomAttributes(propriété, attType)
                If attributs.Length > 0 Then
                    att = DirectCast(attributs(0), DataInfoAttribute)
                    For Each Col As DataColumn In Row.Table.Columns
                        If Col.ColumnName = att.FieldName Then
                            MatchField = True
                            'propriété.SetValue(Me, Value, Nothing)
                            WriteValue(Row, Col.ColumnName, Value)
                            'row(Col) = Value
                            'Debug.WriteLine("Propriété = " & propriété.Name)
                            'Debug.WriteLine("Field Name = " & att.FieldName)
                            'Debug.WriteLine("Field Lenght = " & att.FiedLenght)
                        End If
                    Next
                    If MatchField = False Then
                        Throw New Exception(String.Format("Une Propriété '{0}' du DTO '{1}' n'a pas trouvé le champ du DataRow correspondant", propriété.Name, Me.ToString))
                        Exit Sub
                    End If
                End If
            Next
     
        End Sub
     
        Private Sub SaveProperty(ByVal RowView As DataRowView, ByVal ColChangingName As String, ByVal Value As Object, Optional ByVal SetRequestToDBNull As Boolean = False)
            Dim Row As DataRow = RowView.Row
            'Dim propriété As PropertyInfo
     
            'If Not _DicoAttToProperties.ContainsKey(ColChangingName) Then
            '    Throw New Exception(String.Format("Le dictionnaire des propriétés ne contient pas la clé du champ '{0}' de l'objet métier '{1}'", ColChangingName, Me.ToString))
            '    Exit Sub
            'Else
            'Dim Keyprop As String = _DicoAttToProperties(ColChangingName)
            'propriété = _DicoProperties(Keyprop)
            'Dim value As Object = propriété.GetValue(Me, Nothing)
            If SetRequestToDBNull = True Then
                Call WriteDBNull(Row, ColChangingName)
            Else
                Call WriteValue(Row, ColChangingName, Value)
            End If
            'row(ColChangingName) = value
            'End If
     
        End Sub
        Private Function GetTrueValue(ByVal row As DataRow, ByVal Col As DataColumn) As Object
            Dim _Type As Type = Col.DataType
            Dim _TypeCode As TypeCode = Type.GetTypeCode(_Type)
            Dim value As Object = row(Col)
     
            If value Is DBNull.Value Then
                value = Nothing
            Else
                'Value = CType(Value, T)
            End If
     
            If _TypeCode = TypeCode.String Then 'Le type String est un Type réference qui peut prendre la valeur Nothing. Il faut convertir la valeur Nothing en "".
                If value Is Nothing Then value = ""
            Else
                ' T_Defaut = Defaut
            End If
            Return value
        End Function
        Public Sub WriteDBNull(ByVal Row As DataRow, ByVal ColName As String)
            TryToWriteValue(Row, ColName, DBNull.Value)
        End Sub
        Public Sub WriteValue(ByVal Row As DataRow, ByVal ColName As String, ByVal Value As Object)
            If Value Is Nothing Then Value = DBNull.Value
     
            Dim Original As Object = Row(ColName)
            If Original Is Nothing Then
                TryToWriteValue(Row, ColName, Value)
            Else
                If Original.Equals(Value) Then
                Else
                    TryToWriteValue(Row, ColName, Value)
                End If
            End If
        End Sub
        Private Sub TryToWriteValue(ByVal Row As DataRow, ByVal ColName As String, ByVal Value As Object)
            Try
                Row(ColName) = Value
                Mysource1.TraceEvent(TraceEventType.Verbose, 0, String.Format("### Ecriture ### ColName = '{0}' - Value = '{1}'", ColName, Value.ToString))
            Catch ex As Exception
                MessageBox.Show(ex.Message, "Echec d'écriture", MessageBoxButtons.OK, MessageBoxIcon.Error)
            End Try
        End Sub
     
    End Class
    DTO:
    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
    Public Class DTOTache
        Inherits DTOBase
     
        Public Sub New()
            MyBase.New()
     
        End Sub
        Public Sub New(RowView As DataRowView)
            MyBase.New(RowView)
        End Sub
     
        Private _IdTache As Integer
        <DataInfo("ID_TACHE")> _
        Public Property IdTache() As Integer
            Get
                Return _IdTache
            End Get
            Set(ByVal value As Integer)
                If Not MyBase.NotifyPropertyChanging("IdTache", _IdTache, value) Then _IdTache = value
                NotifyPropertyChanged("IdTache", _IdTache)
            End Set
        End Property
        Private _IdProjet As Integer
        <DataInfo("ID_PROJET")> _
        Public Property IdProjet() As Integer
            Get
                Return _IdProjet
            End Get
            Set(ByVal value As Integer)
                If Not MyBase.NotifyPropertyChanging("IdProjet", _IdProjet, value) Then _IdProjet = value
                NotifyPropertyChanged("IdProjet", _IdProjet)
            End Set
        End Property
        Private _catégorie As String
        <DataInfo("CATEGORIE")> _
        Public Property Catégorie() As String
            Get
                Return _catégorie
            End Get
            Set(ByVal value As String)
                If Not MyBase.NotifyPropertyChanging("Catégorie", _catégorie, value) Then _catégorie = value
                NotifyPropertyChanged("Catégorie", _catégorie)
            End Set
        End Property
        Private _Sujet As String
        <DataInfo("SUJET")> _
        Public Property Sujet() As String
            Get
                Return _Sujet
            End Get
            Set(ByVal value As String)
                If Not MyBase.NotifyPropertyChanging("Sujet", _Sujet, value) Then _Sujet = value
                NotifyPropertyChanged("Sujet", _Sujet)
            End Set
        End Property
        Private _Priorité As String
        <DataInfo("PRIORITE")> _
        Public Property Priorité() As String
            Get
                Return _Priorité
            End Get
            Set(ByVal value As String)
                If Not MyBase.NotifyPropertyChanging("Priorité", _Priorité, value) Then _Priorité = value
                NotifyPropertyChanged("Priorité", _Priorité)
            End Set
        End Property
        Private _Echéance As DateTime
        <DataInfo("ECHEANCE")> _
        Public Property Echéance() As DateTime
            Get
                Return _Echéance
            End Get
            Set(ByVal value As DateTime)
                If Not MyBase.NotifyPropertyChanging("Echéance", _Echéance, value) Then _Echéance = value
                NotifyPropertyChanged("Echéance", _Echéance)
            End Set
        End Property
        Private _ValideAT As Boolean
        <DataInfo("VALIDE_AT")> _
        Public Property ValideAT() As Boolean
            Get
                Return _ValideAT
            End Get
            Set(ByVal value As Boolean)
                If Not MyBase.NotifyPropertyChanging("ValideAT", _ValideAT, value) Then _ValideAT = value
                NotifyPropertyChanged("ValideAT", _ValideAT)
            End Set
        End Property
        Private _ValideCP As Boolean
        <DataInfo("VALIDE_CP")> _
        Public Property ValideCP() As Boolean
            Get
                Return _ValideCP
            End Get
            Set(ByVal value As Boolean)
                If Not MyBase.NotifyPropertyChanging("ValideCP", _ValideCP, value) Then _ValideCP = value
                NotifyPropertyChanged("ValideCP", _ValideCP)
            End Set
        End Property
        Private _Terminé As Boolean
        <DataInfo("TERMINE")> _
        Public Property Terminé() As Boolean
            Get
                Return _Terminé
            End Get
            Set(ByVal value As Boolean)
                If Not MyBase.NotifyPropertyChanging("Terminé", _Terminé, value) Then _Terminé = value
                NotifyPropertyChanged("Terminé", _Terminé)
            End Set
        End Property
        Private _Type As String
        <DataInfo("TYPE")> _
        Public Property Type() As String
            Get
                Return _Type
            End Get
            Set(ByVal value As String)
                If Not MyBase.NotifyPropertyChanging("Type", _Type, value) Then _Type = value
                NotifyPropertyChanged("Type", _Type)
            End Set
        End Property
        Private _Statut As String
        <DataInfo("STATUT")> _
        Public Property Statut() As String
            Get
                Return _Statut
            End Get
            Set(ByVal value As String)
                If Not MyBase.NotifyPropertyChanging("Statut", _Statut, value) Then _Statut = value
                NotifyPropertyChanged("Statut", _Statut)
            End Set
        End Property
        Private _Message As String
        <DataInfo("MESSAGE")> _
        Public Property Message() As String
            Get
                Return _Message
            End Get
            Set(ByVal value As String)
                If Not MyBase.NotifyPropertyChanging("Message", _Message, value) Then _Message = value
                NotifyPropertyChanged("Message", _Message)
            End Set
        End Property
        Private _Auteur As String
        <DataInfo("AUTEUR")> _
        Public Property Auteur() As String
            Get
                Return _Auteur
            End Get
            Set(ByVal value As String)
                If Not MyBase.NotifyPropertyChanging("Auteur", _Auteur, value) Then _Auteur = value
                NotifyPropertyChanged("Auteur", _Auteur)
            End Set
        End Property
        Private _DateCréation As DateTime
        <DataInfo("DATE_CREATION")> _
        Public Property DateCréation() As DateTime
            Get
                Return _DateCréation
            End Get
            Set(ByVal value As DateTime)
                If Not MyBase.NotifyPropertyChanging("DateCréation", _DateCréation, value) Then _DateCréation = value
                NotifyPropertyChanged("DateCréation", _DateCréation)
            End Set
        End Property
        Private _EditéPar As String
        <DataInfo("EDITE_PAR")> _
        Public Property EditéPar() As String
            Get
                Return _EditéPar
            End Get
            Set(ByVal value As String)
                If Not MyBase.NotifyPropertyChanging("EditéPar", _EditéPar, value) Then _EditéPar = value
                NotifyPropertyChanged("EditéPar", _EditéPar)
            End Set
        End Property
        Private _DateEdit As DateTime
        <DataInfo("DATE_EDIT")> _
        Public Property DateEdit() As DateTime
            Get
                Return _DateEdit
            End Get
            Set(ByVal value As DateTime)
                If Not MyBase.NotifyPropertyChanging("DateEdit", _DateEdit, value) Then _DateEdit = value
                NotifyPropertyChanged("DateEdit", _DateEdit)
            End Set
        End Property
     
    End Class
    BLL:
    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
    Public NotInheritable Class BLLTacheManager
        'Inherits DTOTache
     
        Private Sub New()
            _DTO = New DTOTache
            Call LoadDTO(_DTO)
            _IsSilentMode = False
        End Sub
     
        Private Shared s_Instance As BLLTacheManager
        Private Shared s_InstanceLocker As New Object()
        ' Singleton
        Public Shared ReadOnly Property GetInstance() As BLLTacheManager
            Get
                SyncLock s_InstanceLocker
                    If s_Instance Is Nothing Then
                        s_Instance = New BLLTacheManager()
                    End If
                    Return s_Instance
                End SyncLock
            End Get
        End Property
     
        Public Sub LoadDTO(DTO As DTOTache)
            RemoveHandler _DTO.DataPropertyChanging, AddressOf PropertyChanging
            RemoveHandler _DTO.DataPropertyChanged, AddressOf PropertyChanged
            _DTO = DTO
            AddHandler _DTO.DataPropertyChanging, AddressOf PropertyChanging
            AddHandler _DTO.DataPropertyChanged, AddressOf PropertyChanged
            'Bs.DataSource = _DTO
        End Sub
     
        Public Sub SaveDTO()
            If Not _DTO Is Nothing Then _DTO.Save()
        End Sub
     
        Public Sub SaveDTO(ByVal ColName As String)
            If Not _DTO Is Nothing Then _DTO.Save(ColName)
        End Sub
     
        Private _DTO As DTOTache
        Public Property Item() As DTOTache
            Get
                Return _DTO
            End Get
            Set(ByVal value As DTOTache)
                _DTO = value
                Call LoadDTO(_DTO)
                'Bs.DataSource = _DTO
            End Set
        End Property
        Private _IsSilentMode As Boolean
        Public Property IsSilentMode() As Boolean
            Get
                Return _IsSilentMode
            End Get
            Set(ByVal value As Boolean)
                _IsSilentMode = value
            End Set
        End Property
     
        Public Sub CalculDTO()
            If Not _DTO Is Nothing Then
                PropertyChanged(_DTO, Nothing)
            Else
                Throw New Exception(String.Format("le DTO n'a pas été chargé dans la Bll {0}", Me.ToString))
                Exit Sub
            End If
        End Sub
        Public Sub CalculDTO(ByVal DTO As DTOTache)
            If Not DTO Is Nothing Then
                PropertyChanged(DTO, Nothing)
            Else
                Throw New Exception(String.Format("le DTO a calculer dans la Bll {0} est Null", Me.ToString))
                Exit Sub
            End If
        End Sub
        Private _PropertyChanging As Boolean
        Private Sub PropertyChanging(Sender As Object, ByVal e As DataEventArgspropertyChanging) ' Handles _DTO.NotifyPropertyChange
            If _PropertyChanging = True Then Exit Sub
            _PropertyChanging = True
            'Console.Beep()
            Mysource1.TraceEvent(TraceEventType.Information, 0, String.Format("\\\ PropetyChanging /// PropertyName = '{0}' - OriginalValue = '{1}' - ProposedValue = '{2}'", e.PropertyName, e.OriginalValue.ToString, e.ProposedValue.ToString))
     
            Dim ErrorMessage As String = String.Empty
            Dim Isvalide1 As Boolean = False
            Dim Isvalide2 As Boolean = False
            Dim ReturnedValue As Object = Nothing
            Dim KeyAtt As String = _DTO.GetAttributsByProp(e.PropertyName)
            Dim ErrorText As String = ""
            Dim WriteDateTime As Boolean = True
            If Not KeyAtt Is Nothing Then
                Dim Attribut As DataInfoAttribute = _DTO.Attributs(KeyAtt)
                'Dim ColChangingName As String = Attribut.FieldName
                'Dim Prop As propertyinfo
                If Not IsValideData2(Attribut, e, ReturnedValue, True, IsSilentMode, ErrorText) Then
                    ErrorMessage = ErrorText
                Else
                    Isvalide1 = True
                End If
     
            End If
     
     
            Select Case e.PropertyName
                Case "Sujet"
                    If Not IsAuteur(IsSilentMode, ErrorMessage) Then
                    ElseIf CStr(e.ProposedValue) = "Toto1" Then
                        If IsSilentMode = False Then MessageBox.Show("Le texte Toto1 n'est pas autorisé !", "", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                        ErrorMessage = "Le texte Toto1 n'est pas autorisé !"
                    Else
                        Isvalide2 = True
                    End If
                Case "Catégorie"
                    If Not IsAuteur(IsSilentMode, ErrorMessage) Then
                    Else
                        Isvalide2 = True
                    End If
                Case "Priorité"
                    If Not IsAuteur(IsSilentMode, ErrorMessage) Then
                    Else
                        Isvalide2 = True
                    End If
                Case "Type"
                    If Not IsAuteur(IsSilentMode, ErrorMessage) Then
                    ElseIf CStr(e.ProposedValue) = ListeTYPE(2) And _DTO.Terminé = True And (_DTO.ValideAT = False Or _DTO.ValideCP = False) Then
                        If IsSilentMode = False Then MessageBox.Show("Le statut 'terminé' à été a été annulé,  La tache doit être validée avant d'être terminé !", "", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                        _DTO.Terminé = False
                        Isvalide2 = True
                    Else
                        Isvalide2 = True
                    End If
                Case "Echéance"
                    If Not IsAuteur(IsSilentMode, ErrorMessage) Then
                    Else
                        Isvalide2 = True
                    End If
                Case "ValideAT"
                    If Not IsAuteur(IsSilentMode, ErrorMessage) Then
                    ElseIf CBool(e.ProposedValue) = False And _DTO.Terminé = True Then
                        If IsSilentMode = False Then MessageBox.Show("Vous ne pouvez pas modifier le statut des taches Terminé !", "", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                        ErrorMessage = "Vous ne pouvez pas modifier le statut des taches Terminé !"
                    Else
                        Isvalide2 = True
                    End If
                Case "ValideCP"
                    If Not IsAuteur(IsSilentMode, ErrorMessage) Then
                    ElseIf CBool(e.ProposedValue) = False And _DTO.Terminé = True Then
                        If IsSilentMode = False Then MessageBox.Show("Vous ne pouvez pas modifier le statut des taches Terminé !", "", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                        ErrorMessage = "Vous ne pouvez pas modifier le statut des taches Terminé !"
                    Else
                        Isvalide2 = True
                    End If
                Case "Terminé"
                    If Not IsAuteur(IsSilentMode, ErrorMessage) Then
                    ElseIf CBool(e.ProposedValue) = True And (_DTO.ValideAT = False Or _DTO.ValideCP = False) And _DTO.Type = ListeTYPE(2) Then
                        If IsSilentMode = False Then MessageBox.Show("La tache doit être validée avant d'être terminé !", "", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                        ErrorMessage = "La tache doit être validée avant d'être terminé !"
                    Else
                        Isvalide2 = True
                    End If
     
                Case Else
                    Isvalide2 = True
                    WriteDateTime = False
            End Select
     
            'Mysource1.TraceEvent(TraceEventType.Verbose, 0, TEXTLOG_EXITING_SUB, ProfilTrace.StopChrono)
     
            If Isvalide1 = True And Isvalide2 = True Then
                e.ErrorMessage = ""
                If Not KeyAtt Is Nothing And WriteDateTime = True Then
                    _DTO.EditéPar = MyUser.Name
                    _DTO.DateEdit = DateTime.Now
                End If
                Mysource1.TraceEvent(TraceEventType.Information, 0, String.Format("Les changements pour la propriété = '{0}' ont été validés avec la valeur {1}", e.PropertyName, e.ProposedValue.ToString))
            Else
                e.Cancel = True
                e.ErrorMessage = ErrorMessage
                'Return False
                Mysource1.TraceEvent(TraceEventType.Information, 0, String.Format("Les changements pour la propriété = '{0}' ont été rejetés", e.PropertyName))
            End If
            _PropertyChanging = False
        End Sub
     
        Private Sub PropertyChanged(Sender As Object, ByVal e As DataEventArgs)
            If _PropertyChanging = True Then Exit Sub
            _PropertyChanging = True
            'Console.Beep()
            Mysource1.TraceEvent(TraceEventType.Verbose, 0, String.Format("*** Calcul du DTO *** '{0}' pour la tache : '{1}'", _DTO.ToString, _DTO.IdTache))
            'Debug.WriteLine("Calcul du DTO = " & Sender.ToString)
     
            Dim Today As System.DateTime = DateTime.Now
            Dim BolValide As Boolean
            Dim BolRetard As Boolean
     
            If _DTO.ValideAT = False Or _DTO.ValideCP = False Then
                BolValide = False ' = "A Valider"
            Else
                BolValide = True
            End If
     
            If _DTO.Echéance <> Nothing Then
                If CDate(_DTO.Echéance) < Today.Date Then
                    BolRetard = True ' = "Retard"
                Else
                    BolRetard = False
                End If
            Else
                'DateECHEANCE.SetRequestToDBNull()
                _DTO.Echéance = Nothing
            End If
     
     
            If _DTO.Type = ListeTYPE(0) Then
                'DateECHEANCE.SetRequestToDBNull()
                _DTO.Echéance = Nothing
                'strSTATUT.SetRequestToDBNull()
                _DTO.Statut = Nothing
                'DateECHEANCE.Current = Nothing
                'strSTATUT.Current = Nothing
     
            ElseIf _DTO.Type = ListeTYPE(1) Then
                ''''''DateECHEANCE.Current = DateECHEANCE.Current
                If BolRetard = True Then _DTO.Statut = ListeSTATUT_Taches(1)
                If BolRetard = False Then _DTO.Statut = "En cours"
     
                If _DTO.Terminé = True Then _DTO.Statut = ListeSTATUT_Taches(0)
     
            ElseIf _DTO.Type = ListeTYPE(2) Then
                ''''''DateECHEANCE.Current = DateECHEANCE.Current
                If BolRetard = False Then _DTO.Statut = "En cours"
     
                If BolValide = True Then _DTO.Statut = "Validé"
                If BolValide = False Then _DTO.Statut = ListeSTATUT_Taches(2)
     
                If BolRetard = True Then _DTO.Statut = ListeSTATUT_Taches(1)
     
                If _DTO.Terminé = True Then _DTO.Statut = ListeSTATUT_Taches(0)
     
            End If
     
            _PropertyChanging = False
        End Sub
        Private Function IsAuteur(ByVal IsSilentMode As Boolean, ByRef ErrorMessage As String) As Boolean
            If _DTO.Auteur <> MyUser.Name Then
                If IsSilentMode = False Then MessageBox.Show("Vous n'êtes pas l'auteur de cette tache !", "", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                ErrorMessage = "Vous n'êtes pas l'auteur de cette tache !"
                Return False
            Else
                Return True
            End If
        End Function
     
    End Class

  9. #9
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2012
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2012
    Messages : 640
    Points : 372
    Points
    372
    Par défaut
    Bonsoir à tous,
    Je fait remonter ce post qui m’intéresse beaucoup. En effet je me pose un tas de question sur le développement en couche mais je n'en poserez que 2 pour allez à l'essentiel :
    - Déjà je crains d'utiliser un modèle de DTO qui ne respecte pas les standards du développement en couche (voir mon code ci-dessus).
    En fait pour respecter les conventions il est dit que le DTO est un objet de transfert, lui-même n'a pas vocation à être référencé par aucune couche mais en revanche il peut(doit) être référencé par toutes les couches (GUI, BLL, DAL). Hors si je regarde de plus prêt mon DTO celui-ci ne référence aucune couche (du moins je ne crois pas) mais il porte des informations provenant de la BDD (Noms, type et longueurs des champs de la BDD), cela fait t'il une différence qui me permettrait de dire que je respecte les standards du développement en couche ?

    - Autre question, y a t'il une couche de prédilection (ou obligatoire) devant être utilisée pour établir les relations(associations) 'Champ de BDD'<->'Propriété de l'objet' ? (Comme vous pouvez le voir dans mon code j'établie toutes les relations dans mon DTO).

    Merci beaucoup si quelqu'un peux m’éclairer sur ces 2 points.

  10. #10
    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
    Hello,

    Je suis loin d'être un expert mais je vais tenter de te répondre. De ce fait, ne prend pas mes réponses pour argent comptant. Il est possible que j'y raconte l'une ou l'autre ânerie .

    Déjà, il y a développement en couche et développement en couche. Sauf erreur de ma part, tous n'utilise pas les DTO, certains ajoute une couche (web)service, d'autres pas. Bref, à voir selon ce que tu as besoin.

    Personnellement, je n'utilise pas que le modèle avec les DTO (et je ne parlerai que celui-ci vu que c'est celui qui t'intéresse). Pour rappel, il se représente comme ceci.
    Nom : couche.png
Affichages : 514
Taille : 3,2 Ko(une couche supplémentaire de service peut venir s’intercaler entre GUI et BLL)

    Donc, la couche GUI (Graphical User Interface) utilise la couche BLL (Business Logic Layer), la couche BLL utilise la couche DAL (Data Access Layer) et ces trois couches utilisent toutes la couche DTO (Data Transfert Object) car ce sont les objets de DTO qui passent entre les couches.

    Si on veut coller rigoureusement à cette conception, les DTO se doivent d'être les plus légers possible. C'est-à-dire ne contenir que des propriétés et un constructeur. Pourquoi ? Justement car ce sont ces objets qui sont passé d'une couche à l'autre. Pour mieux comprendre, il faut regarder à cela d'un point de vue physique. Si toutes les dll générées par ces couches se trouvent au final sur la même machine, la taille du DTO importe peu. Mais il est tout à fait possible que les DLL soient sur des machines différentes et parfois très distantes. Du coup, afin d'accélérer les communications réseaux, il vaut mieux avoir des objets DTO les plus petits possibles.

    Ca, c'est pour la théorie. Maintenant, je trouve personnellement qu'il ne faut pas être extrémiste (intégriste?). Dans mes DTO, il y a la plus part du temps une méthode ToString(). Alors oui, ça ne colle plus à la théorie mais ce n'est pas une méthode de max 5 lignes qui va tuer les performances de l'application car les communications réseaux prendront trop de temps. Les avantages de l'ajout de cette méthode compensant largement ces quelques octets supplémentaires transitant entre les couches. Donc à toi de voir ce qui colle à tes besoins et si ça ne pénalise pas le fonctionnement de l'applicatif.

    Pour ta deuxième question, je dirais que cela se passe dans la couche DAL. Tes DTO n'ont pas à savoir d'où viennent les valeurs leurs propriétés. Tout ce qui les intéressent, c'est d'avoir les bonnes valeurs. C'est à la couche DAL de s'occuper de cela et de s'assurer que la bonne valeur va bien dans la bonne propriétés du DTO. C'est dans cette couche qu'on va interroger la base de données (ou toute autre source de données), récupérer les informations dont on a besoin et les mettre dans le DTO qui va bien. DTO qui sera renvoyé à la couche BLL.

    J'espère t'avoir répondu et ne pas avoir écrit trop de bêtises.
    Kropernic

  11. #11
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2012
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2012
    Messages : 640
    Points : 372
    Points
    372
    Par défaut
    Bonsoir Kropernic, merci de m'avoir répondu.
    Plus je découvre le développement en couches plus je m'appercois que bien que les principes et la base soit la même il n'existe pas de code éprouvé qui fait l'unanimité. Au contraire j'ai le sentiment que les solutions de chacun sont relativement différentes.
    Mon inquiétude était de faire du code complètement en dehors d'un hypothétique standard mais me voilà un peu rassuré.

    Donc à toi de voir ce qui colle à tes besoins et si ça ne pénalise pas le fonctionnement de l'applicatif.
    Tout bien réfléchie mon DTO n'est pas voué à transiter sur un réseau et il me semble que ma solution permet d’éviter de dupliquer l'écriture des propriétés des DTO dans la DAL afin d'établir les associations 'Champ de BDD'<->'Propriété de l'objet'. C'est un soucis en moins en cas d'oublie d'une propriété me semble t'il. Et en réalité je ne suis même pas sûre d'avoir une DAL dans mon programme (ou d'en avoir besoin) puisque j'utilise un Dataset qui ma foi fait son boulot et que j'ai très peu de code autour de ce Dataset.

    J'ajouterais une dernière question si je peux me permettre :
    Entre la GUI et le DAL il n'y a donc aucune relation directe. Hors lorsque l'on veux mettre en place la validation des saisies utilisateurs et qui concerne les limitations de la BDD (je pense notamment aux champs avec un nombre de caractères limité). Pour cela il faut 'remonter' ces informations de la BDD jusqu'à la couche qui va traiter cette validation. Celle-ci se fait dans la GUI ou dans la BLL ? si c'est dans la BLL apriori ça à l'air facile puisque la BLL communique avec la DAL (si je ne dit pas de bêtises ces limitations sont facilement accessible dans la DAL) mais si la validation doit se faire dans la GUI je ne vois pas comment cette couche peut avoir accès a ces limitations sauf en passant par le DTO.

    Merci beaucoup si je peut avoir d'autres renseignements complémentaires.

  12. #12
    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
    Y un truc qui me chipote dans ce que tu racontes...

    Pourquoi dois-tu aller chercher la taille limite d'une donnée de type caractère dans la DB ? Chez moi, cette taille est définie par des règles business et donc directement connues par la couche BLL.
    Jusqu'ici, je triche en testant directement contre la bonne valeur et si elle était amenée à changer, ça pourrait me poser problème...

    Mais j'imagine qu'en créant une classe statique ne contenant que des constantes où tout cela serait défini, ça irait super bien. Non ?
    Kropernic

  13. #13
    Modérateur
    Avatar de DotNetMatt
    Homme Profil pro
    CTO
    Inscrit en
    Février 2010
    Messages
    3 611
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : CTO
    Secteur : Finance

    Informations forums :
    Inscription : Février 2010
    Messages : 3 611
    Points : 9 743
    Points
    9 743
    Billets dans le blog
    3
    Par défaut
    Le DataSet ne fait pas partie des standards, pour la simple raison que ça ne répond pas aux mêmes besoins. Pour moi le DataSet c'est OK pour faire du quick and dirty quand on a pas le temps de faire une vraie DAL. Avec le DataSet, tu dois te trimballer tout un jeu de données en mode déconnecté, ce qui implique une gestion particulière des mises à jour/insertions. Avec une DAL classique, dès qu'un utilisateur clique sur Save, hop on insère/on met à jour. Concernant les données, on se trimbale avec le strict nécessaire et surtout on peut déférer l'exécution des requêtes via IQueryable.

    Pour répondre à tes interrogations BasicZX81, il faut déjà se remettre dans le contexte : pourquoi utilise-t-on ce découpage en 3 couches ? Ce découpage permet (entre autre) d'isoler le code, de mieux l'organiser au sein de la solution et aussi de pouvoir livrer indépendamment la DAL de la BLL ou de l'UI (en admettant que chaque couche soit un projet différent dans Visual Studio). Il permet aussi de découpler au maximum les interactions. Cela signifie que dans l'absolu, je pourrais utiliser ma BLL dans plusieurs projets, avec une DAL et une/des UI différentes. Ce découplage permet également de pouvoir consommer la BLL via différents clients (WinForms/WPF, ASP.NET, client mobile, etc.) ou encore de permettre à la BLL de pouvoir consommer la DAL, peu importe la source de données sous-jacente, en la rendant le plus générique possible.

    Voilà, dans ce contexte, si on reprend les principes de la POO, une classe a son propre comportement et doit être spécialisée. Dans le cas des DTO, la spécialisation est de transporter les données dans les différentes couches. C'est la seule chose que ces objets doivent faire. Ensuite au niveau du comportement, pour des cas spécifiques, on peut redéfinir le ToString, le Hashcode, etc. Ca, c'est le standard. Ensuite bien sûr tu es libre de faire ce que tu veux mais tu ne seras plus dans le standard : plus tu vas ajouter de choses sur ces DTO, plus tu vas contraindre leur utilisation. A mon sens, il faut rester dans la loi de Pareto (80/20) : 80% de standard, et jusqu'à 20% de personnalisation, et surtout avoir une documentation claire pour que ton successeur puisse reprendre la main et comprendre ce que tu as fait, et pourquoi.

    Concernant le référencement, les DTO sont référencés et utilisés par tous les projets de la solution (ou presque, exemple d'exception : un Framework ou une librairie tierce). Le DTO ne référence rien, sauf éventuellement un Framework ou une librairie tierce).

    Pour la validation au niveau de la BLL, il n'y a pas de solution miracle, si tu changes la longueur d'un champ en base, il faut répercuter ce changement dans le code. Il y a différentes façons de faire, soit à laide d'un genre de moteur de règles dans la BLL, soit en décorant simplement les propriétés des DTO avec des attributs, puis lors de la validation, analyser ces attributs et traiter en conséquence. Cette seconde solution est à mon sens la plus simple à gérer.

    Ensuite, concernant les remarques de Kropernic, je ne suis pas d'accord avec la considération de la "lourdeur" du DTO en cas de transit réseau. Les DTO peuvent être complexes si le projet l'exige. Ensuite il existe des solutions pour optimiser le transport sur le réseau, comme par exemple la compression.

    Voici en gros les principales responsabilités de la BLL :
    • Traiter les commandes en provenance de l'UI
    • Afficher des données (grâce à l'UI)
    • Coordonner les workflows
    • Prendre des décisions logiques
    • Effectuer des calculs
    • Maintenir l'état de l'application
    • Persister des données (grâce à la DAL)
    • Récupérer les données (grâce à la DAL)


    Pour finir, il n'y a pas une seule et unique façon de faire, chaque projet ayant ses particularités, et chaque développeur ne raisonnant pas de la même façon. A mon sens, si ces standards existent, c'est qu'il y a des raisons donc soit on choisi de les appliquer, soit on s'en passe. Faire de l'à-peu-près peut être dangereux et coûteux pour la maintenance.
    On le voit par exemple avec les méthodos Agile. Beaucoup d'équipes en France les appliquent mais pas dans leur totalité, au final on se retrouve avec un mix de plusieurs méthodos, par exemple Scrum + Kanban, ce qui mène à des galères tant au niveau des outils que de la gestion de projet... Cela pour illustrer le fait que si les standards existent, c'est qu'il y a de bonnes raisons. A moins d'être en mesure d'inventer son propre standard, et de le faire accepter par la communauté, je pense que le plus de sage est d'essayer de les comprendre et de les utiliser en leur restant le plus fidèle possible, et en ne s'en écartant que lorsqu'il n'y a pas d'autre solution, tout en respectant la loi de Pareto (80/20).

    Pour illustrer l'intérêt de la loi de Pareto, je prendrais un exemple que l'on constate très souvent, autour de la mise en places des ERP (type SharePoint, CRM, etc.). Lorsqu'on ne respecte pas la loi de Pareto (80% de natif, et jusqu'à 20% de personnalisation), on se heurte à de gros problèmes de migration. Lorsque la version majeure suivante est publiée, et que le projet est constitué de 30% de personnalisation, bien souvent, la structure de l'ERP a été trafiquée afin de pouvoir répondre à un besoin du client. Or, ce traficage va complexifier et augmenter le coût de la migration vers la nouvelle version. Du coup, on voit encore de très vieilles versions dans les entreprises, ou des projets de migration qui sont prolongés coup sur coup, ce qui conduit à des dépenses hallucinantes.
    C'est le même principe pour le respect des architectures standard. Si tu as 30% de personnalisation, l'équipe qui prendra ta relève sur ton projet va mettre plus de temps à comprendre les 10% de personnalisation que tu as rajouté sur le projet, et peut-être (sûrement) introduire des contraintes qui vont leur rendre la vie bien plus compliquée lorsqu'il s'agira de refondre tel ou tel module de l'application.
    Less Is More
    Pensez à utiliser les boutons , et les balises code
    Desole pour l'absence d'accents, clavier US oblige
    Celui qui pense qu'un professionnel coute cher n'a aucune idee de ce que peut lui couter un incompetent.

  14. #14
    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 DotNetMatt Voir le message
    Ensuite, concernant les remarques de Kropernic, je ne suis pas d'accord avec la considération de la "lourdeur" du DTO en cas de transit réseau. Les DTO peuvent être complexes si le projet l'exige. Ensuite il existe des solutions pour optimiser le transport sur le réseau, comme par exemple la compression.
    Tu n'es pas d'accord mais tu ne me contredis pas non plus ^^. Bien sûr qu'un DTO peut être complexe si le projet l'exige mais il restera tout de même le plus léger possible.

    Un exemple tiré de mon projet en cours (une application d'encodage des promotions pour des magasins). Il va y avoir un DTO pour représenter les magasins comme suit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Public Class Store
        Public Property Id As Byte
        Public Property Code As String
        Public Property Name As String
     
        Public Sub New(id As Byte, code As String, name As String)
            Me.Id = id
            Me.Code = code
            Me.Name = name
        End Sub
    End Class
    Bref, vraiment un objet super simple.

    A côté de ça, l'objet promotion sera nettement plus complexe avec une liste de magasin, une liste de jour, des listes d'articles et bien d'autres choses. Mais toutes les propriétés que cet objet contiendra ne seront que celles qui lui sont nécessaires et suffisantes. Rien de plus, rien de moins ! Donc ça reste un objet léger à faire transiter sur le réseau pour les cas où cela est à prendre en compte. Non ?
    Certes il y a la compression mais un petit objet compressé sera toujours plus petit qu'un gros objet compressé (enfin je crois).
    Kropernic

  15. #15
    Modérateur
    Avatar de DotNetMatt
    Homme Profil pro
    CTO
    Inscrit en
    Février 2010
    Messages
    3 611
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : CTO
    Secteur : Finance

    Informations forums :
    Inscription : Février 2010
    Messages : 3 611
    Points : 9 743
    Points
    9 743
    Billets dans le blog
    3
    Par défaut
    En effet, le DTO reste un "objet léger" c'est d'ailleurs tout l'avantage par rapport au DataSet. En me relisant et en lisant ta réponse je me rend compte que je me suis mal expliqué, mais on est bien d'accord que c'est un objet léger, sans comportement particulier
    Less Is More
    Pensez à utiliser les boutons , et les balises code
    Desole pour l'absence d'accents, clavier US oblige
    Celui qui pense qu'un professionnel coute cher n'a aucune idee de ce que peut lui couter un incompetent.

  16. #16
    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 DotNetMatt Voir le message
    En effet, le DTO reste un "objet léger" c'est d'ailleurs tout l'avantage par rapport au DataSet. En me relisant et en lisant ta réponse je me rend compte que je me suis mal expliqué, mais on est bien d'accord que c'est un objet léger, sans comportement particulier
    Ca m'étonnait aussi qu'on soit en désaccord sur ce point ^^.
    Kropernic

  17. #17
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2012
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2012
    Messages : 640
    Points : 372
    Points
    372
    Par défaut
    Merci à vous Kropernic et DotNetMatt pour cette discussion vraiment très intéressante,
    Grace à vous je commence à voir quelques points faibles et je sais déjà ce que je devrais améliorer dans mon architecture.

    Pourquoi dois-tu aller chercher la taille limite d'une donnée de type caractère dans la DB ? Chez moi, cette taille est définie par des règles business et donc directement connues par la couche BLL.
    Jusqu'ici, je triche en testant directement contre la bonne valeur et si elle était amenée à changer, ça pourrait me poser problème...
    Pour la validation au niveau de la BLL, il n'y a pas de solution miracle, si tu changes la longueur d'un champ en base, il faut répercuter ce changement dans le code. Il y a différentes façons de faire, soit à laide d'un genre de moteur de règles dans la BLL, soit en décorant simplement les propriétés des DTO avec des attributs, puis lors de la validation, analyser ces attributs et traiter en conséquence. Cette seconde solution est à mon sens la plus simple à gérer.
    En effet, cela réponds exactement a mes interrogations et à ce sujet je viens de découvre une faille dans mon DTO. Mes attributs de BDD ne sont lus qu'à condition que je crée mon DTO avec le New(RowView As DataRowView) puisque c'est le DataRowView qui me donne ces infos. Avec le New sans arguments ça ne fonctionne pas. Ce détail plus vos remarques m'ont persuadé que je devrais coder les attributs en dur.
    Donc si j'ai bien suivi Kropernic tu le fait dans la BLL et DotNetMatt tu le fait en décorant le DTO.

    Le point ci-dessus plus le fait que je devrais faire mes associations 'Champ de BDD'<->'Propriété de l'objet' dans la DAL font qu'il ne restera quasiment plus rien dans mon DTOBase. Il fallait bien ces 2 questions pour que je me rapproche petit à petit d'un certain standard et je vous remercie vraiment beaucoup pour m'avoir éclairé sur le développement en couche et les DTO.

    Par contre je pensais que le Dataset était couramment utilisé dans le monde pro pour faire des applis de gestion. Vous ne vous en servait jamais pour faire un cache de données par exemple ou ce genre de chose ? Le Dataset dispose aussi de fonctionnalités comme les mises à jour en cascade, en l'absence de ce Dataset vous remplacé ce mécanisme par du code ou c'est la BDD qui s'en charge automatiquement ? Je ne souhaite pas entrer dans un grand débat mais ça m'interresse.

    Merci à vous.

  18. #18
    Modérateur
    Avatar de DotNetMatt
    Homme Profil pro
    CTO
    Inscrit en
    Février 2010
    Messages
    3 611
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : CTO
    Secteur : Finance

    Informations forums :
    Inscription : Février 2010
    Messages : 3 611
    Points : 9 743
    Points
    9 743
    Billets dans le blog
    3
    Par défaut
    Citation Envoyé par BasicZX81 Voir le message
    Par contre je pensais que le Dataset était couramment utilisé dans le monde pro pour faire des applis de gestion. Vous ne vous en servait jamais pour faire un cache de données par exemple ou ce genre de chose ? Le Dataset dispose aussi de fonctionnalités comme les mises à jour en cascade, en l'absence de ce Dataset vous remplacé ce mécanisme par du code ou c'est la BDD qui s'en charge automatiquement ? Je ne souhaite pas entrer dans un grand débat mais ça m'interresse.
    Personnellement, les seules fois où j'en ai rencontré en entreprise, c'était sur de toutes petites applications de gestion, ou sur des trucs temporaires en mode quick-and-dirty. Sur des applications plus conséquentes, et plus long terme je n'en ai jamais vu (et heureusement).

    Le DataSet est une boite noire, qui embarque tout un tas de fonctionnalités dont tu n'auras pas forcément besoin, et qui peuvent être dangereuses, comme par exemple la gestion des update/insert qui peuvent ne pas être immédiats. Et sans compter le fait qu'avec le dataset tu vas charger tous les attributs, que tu en aies besoin ou non, puis les garder en mémoire. Avec de gros datasets, et l'utilisation de web services, ça peut conduire à des problèmes de performance. Si tu utilises des Listes à la place, c'est beaucoup plus léger, tu ne codes que ce dont tu as besoin et tu sais précisément ce qu'il se passe.

    Après c'est un choix à faire...
    Less Is More
    Pensez à utiliser les boutons , et les balises code
    Desole pour l'absence d'accents, clavier US oblige
    Celui qui pense qu'un professionnel coute cher n'a aucune idee de ce que peut lui couter un incompetent.

  19. #19
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2012
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2012
    Messages : 640
    Points : 372
    Points
    372
    Par défaut
    Ok, Merci beaucoup DotNetMatt

  20. #20
    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
    Bonjour,

    @DotNetMatt
    Citation Envoyé par DotNetMatt Voir le message
    soit en décorant simplement les propriétés des DTO avec des attributs, puis lors de la validation, analyser ces attributs et traiter en conséquence.
    Pourrais-tu montrer comment tu lis l'attribut placé sur une propriété ?

    Pour l'instant j'ai placé un attribut ainsi
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        Private mPersonId As Integer
        <TypePropertyAttribute(GetType(Integer))>
        Public Property PersonId() As Integer
            Get
                Return mPersonId
            End Get
            Set(ByVal value As Integer)
                mPersonId = value
            End Set
        End Property
    Défini dans ma classe d'attribut personnalisé
    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
    ' attribute de propriété
    <System.AttributeUsage(System.AttributeTargets.Property)>
    Public Class TypePropertyAttribute
        Inherits System.Attribute
        Private _TypeProperty As Type
        Sub New(ByVal t As Type)
            _TypeProperty = t
        End Sub
        Function GetTypeProperty() As Type
            Return _TypeProperty
        End Function
        'Public ReadOnly Property TypeProperty As Type
        '    Get
        '        Return _TypeProperty
        '    End Get
        'End Property
    End Class
    Je sais lire tous les attributs (p étant une instance de ma classe personne)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
            For Each pProps As Reflection.PropertyInfo In p.GetType().GetProperties()
                For Each pPropsType As TypePropertyAttribute In pProps.GetCustomAttributes(False)
                    MessageBox.Show(pProps.Name & " " & ((DirectCast(pPropsType, TypePropertyAttribute)).GetTypeProperty).ToString)
                Next
            Next
    Ou un en bidouillant puisque c'est le premier
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
            Dim t As Type = DirectCast(p.GetType().GetProperties()(0).GetCustomAttributes(False)(0), TypePropertyAttribute).GetTypeProperty
    Mais là, je souhaite récupérer l'attribut de ma propriété "PersonId" par son nom. As tu une idée ?
    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.

Discussions similaires

  1. Pourquoi faut-il utiliser des objet DTO ?
    Par khalil88 dans le forum Autres
    Réponses: 0
    Dernier message: 28/05/2012, 16h35
  2. [DAO] Utilisation des DTO
    Par neuromencien dans le forum Autres
    Réponses: 8
    Dernier message: 02/10/2009, 16h15
  3. utilisation des sockets sous windows
    Par Tupac dans le forum Réseau
    Réponses: 2
    Dernier message: 21/12/2002, 18h24
  4. [Crystal Report] Utilisation des vues de sql serveur
    Par Olivierakadev dans le forum SAP Crystal Reports
    Réponses: 2
    Dernier message: 15/11/2002, 17h44
  5. [BCB5] Utilisation des Ressources (.res)
    Par Vince78 dans le forum C++Builder
    Réponses: 2
    Dernier message: 04/04/2002, 16h01

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