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

  1. #1
    Rédacteur/Modérateur

    Quelle est la différence entre un DTO et un POCO ?
    Cette discussion est consacrée à l'article intitulé "Quelle est la différence entre un DTO et un POCO ?" qui est la traduction d'un article de Rudy Lacovara.

    Postez ici vos commentaires concernant cette publication.


  2. #2
    Membre averti
    Excellent article ! très utile. Maintenant, j'ai une petite question :

    Quid du DTO pour lequel on est obligé d'insérer la logique de sérialisation car il implémente IXmlSerializable ? c'est un DTO, un POCO, ni l'un ni l'autre ?

    Sinon, concernant les différents schémas d'implantation, en voici un que j'utilise tous les jours :

    J'ai ma couche d'accès aux données branchée via l'ORM Entitiy Framework.
    J'ai donc une partie DAL me permettant d'effectuer les requetes linq to entities.
    Ensuite, j'ai une parti BLL qui me permet de faire tous les mix de requetes dont j'ai besoin et ressortir un résultat métier.
    Entre la DAL et la BLL, je transforme (de façon bilatérale) mes Entities en DTO et inversement.
    Le tout est au final utilisable avec n'importe quel projet et peut même etre partagé entre projets de types différents.
    Dans mon cas, j'ai branché un webservice WCF à la BLL et il va y avoir une partie ASP.Net MVC2 qui viendra plus tard se brancher sur cette meme BLL.
    Ce qu'il y a d'intéressant avec les DTO, c'est que derriere ils peuvent être importés dans tous les projets se servant de ma BLL (ou meme de mon webservice).

    En tout cas, merci pour l'explication

  3. #3
    Rédacteur/Modérateur

    Citation Envoyé par zax-tfh Voir le message
    Quid du DTO pour lequel on est obligé d'insérer la logique de sérialisation car il implémente IXmlSerializable ? c'est un DTO, un POCO, ni l'un ni l'autre ?
    D'après l'article, je dirais que c'est ni l'un, ni l'autre.

    En effet, il est dit qu'un DTO n'est qu'un conteneur de données sans logique d'aucune sorte ; et qu'un POCO ne contient pas de logique de persistance (concept de "Persistance Ignorance").

  4. #4
    Membre éprouvé
    chez moi toutes les couches d'une application ont accès aux DTO , seul la couche "business" a acces aux POCO ( ou pojo , etc ... ).
    un POCO est forcément lié au business layer,puisqu'il a un comportement.
    Un DTO peut par contre alimenter un template , le modèle , ou n'importe quoi d'autre.

  5. #5
    Inactif  
    Article clair et didactique !

    Je viens donc de réaliser que je suis depuis plus de dix ans un M. Jourdain du POCO/DTO.

    Je ne réponds pas aux questions techniques par MP ! Le forum est là pour ça...


    Une réponse vous a aidé ? utiliser le bouton

    "L’ennui dans ce monde, c’est que les idiots sont sûrs d’eux et les gens sensés pleins de doutes". B. Russel

  6. #6
    Rédacteur/Modérateur

    Citation Envoyé par Bluedeep Voir le message
    Je viens donc de réaliser que je suis depuis plus de dix ans un M. Jourdain du POCO/DTO.
    Oui, avec l'expérience, on tend naturellement vers de bonne pratiques (ou en tous cas, des pratiques éprouvées), sans forcément connaitre les termes qui définissent ces pratiques !

  7. #7
    Membre averti
    Dites moi, dans vos projets, comment faites vous la translation POCO<->DTO ?
    Avez vous des petits outils, des patterns ou autres qui permettent d'automatiser un peu la transformation ? ou bien gérez vous la copie des propriétés à la mano ?

  8. #8
    Rédacteur/Modérateur

    Citation Envoyé par zax-tfh Voir le message
    Dites moi, dans vos projets, comment faites vous la translation POCO<->DTO ?
    Avez vous des petits outils, des patterns ou autres qui permettent d'automatiser un peu la transformation ? ou bien gérez vous la copie des propriétés à la mano ?
    Qu'entends-tu par translation/transformation ?

    Le lien entre un DTO et son POCO correspondant est qu'un POCO a un DTO, les propriétés ne sont pas "copiées" :
    Code C# :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class PersonDTO
    {
        // Propriétés du DTO...
    }
     
    public class PersonPOCO
    {
        public PersonDTO Data { get; set; }
     
        public PersonPOCO(PersonDTO dto) { this.Data = dto; }
    }

    Les propriétés du DTO sont accessibles dans le POCO via la propriété Data.
    Voir l'héritage "a un", dans le paragraphe "Alors qu'est-ce qu'un POCO ?"

  9. #9
    Expert éminent sénior
    Je me joins aux autres pour te féliciter de ton choix de traduction, l'article choisi est excellent, et la traduction de qualité

    bravo

    Mon Blog

    The Cake is still a lie !!!



    Vous voulez contribuer à la rubrique .NET ? Contactez-moi par MP.
    Vous voulez rédiger des articles pour la rubrique .NET ? Voici la procédure à suivre.

  10. #10
    Membre éprouvé
    Confronté à un problème d'architecture, je me suis penché sur cet article.

    Et je me demande comment gérer la composition/agrégation dans ce modèle ?

    En effet, mes objets ont quelques propriétés de type primitif, mais aussi un gros paquet d'instances, voire de collections, d'autre classes.

    Pour être précis sur ma question : à quel niveau gère-t-on la composition ?
    En informatique, le problème se situe toujours entre le clavier et l'écran !
    Il y a deux chemins entre le clavier et l'écran : Par l'UC et par l'utilisateur.

  11. #11
    Invité
    Invité(e)
    Au niveau des POCO tu peux utiliser tous les concepts de la POO sans problèmes mais pour les DTO tu peux aussi le faire mais il faut avant tout savoir que les DTO c'est pour faire transiter des données être un pont entre deux parties ou doit contenir des informations minimalistes dont on a besoin quand on travaille par exemple avec des services telles que WCF. Bref un DTO ne doit pas contenir la complexité de ton modèle ou domaine. Si tu te mets à te poser des questions de composition excédant deux niveaux alors là je ne vois plus l'intérêt d'utiliser les DTO autant utiliser directement ton modèle pour ne pas avoir à tout le recréer en intégrant sa complexité dans les DTO.

  12. #12
    Expert confirmé
    Hello,

    Comme tout le monde l'a déjà dit, il s'agit là d'un excellent article (comme tous les autres (ou presque) du même auteur).

    Par contre cela me pose un souci.

    Avant, la couche BLL passait directement les DTO ou les listes de DTO à la couche GUI qui les utilisait et les renvoyait vers BLL.

    A la lumière de cet article, GUI utilise maintenant des objets de la BLL (des POCO donc) qui contiennent les DTO.

    Mais du coup, comment on fait pour affecter les propriétés DisplayMember et ValueMember des classes du genre ComboBox ou ListBox (pour DisplayMember, j'ai triché en surchargeant la méthode ToString du POCO mais c'est du bricolage non ?)

    Et je n'ai pas encore testé mais j'imagine que je vais avoir le même problème pour affecter la propriété DataPropertyName de la classe DataGridViewColumn.

    Comme faites-vous donc ?
    Kropernic

  13. #13
    Membre averti
    Salut,

    Dans notre projet (Silverlight / WCF) on avait cette problématique étant donné que le fait de recevoir d'une part, une liste de DTO pour alimenter l'itemssource du combobox et, d'autre part, le DTO correspondant à l'élement actuellement sélectionné via le webservice faisait que les deux DTO identiques (celui qui est la sélection courante et celui qui fait partie de la liste et qui en base correspondent a la meme ligne) n'avaient pas la meme adresse mémoire.
    Du coup, en surchargeant la méthode Equals pour faire matcher les property Id plutot que les références mémoire a résolu le problème.

  14. #14
    Expert confirmé
    Euh... J'ai pas compris
    Kropernic

  15. #15
    Membre averti
    Comment utilises tu ta combo, peux tu envoyer un exemple ? parce que je ne sais meme pas dans quel type de projet tu bosses (winforms, wpf, silverlight, win8, wp8 ?)

  16. #16
    Expert confirmé
    Je fais du winforms.

    Mais je pense que je faisais de la merde (j'ai contourné le problème en faisant autrement et ça va mieux maintenant ^^).

    Exemple de ce que je faisais :
    J'avais un poco Person avec une propriété DTO as PersonDTO et un poco Persons qui héritait de List(Of Person).
    Du coup, dans la GUI, j'utilisais des objets du type Persons pour manipuler des listes. Mais ce genre de truc, c'est pas pratique à passer en datasource.

    Maintenant, le poco Persons n'hérite plus de List(Of PersonDTO) mais a une propriété DTO as List(Of PersonDTO). Et ça va tout de suite mieux ^^
    Kropernic

  17. #17
    Futur Membre du Club
    Désolé de déterrer ce topic que je découvre tardivement.

    Est-ce que par hasard un développeur fainéant comme moi aurait créé un outil pour automatiser la création des classes décrites dans cet article très intéressant pour une base de données entière ?

    Qui ne tente rien n'a rien

  18. #18
    Expert confirmé
    Il y a des outils qui existent déjà comme entity framework mais personnellement, je n'aime pas.

    Dans mon ancien taff, j'avais développé ça en me basant sur le catalogue de la db relationnelle utilisée mais cela nécessite d'avoir une db fortement normalisée pour avoir quelque chose de propre. Ca m'avait pris 1/2 journée pour avoir qqch de fonctionnelle donc rien d'insurmontable
    Kropernic

  19. ###raw>post.musername###
    Futur Membre du Club
    Citation Envoyé par Kropernic Voir le message
    Il y a des outils qui existent déjà comme entity framework mais personnellement, je n'aime pas.

    Dans mon ancien taff, j'avais développé ça en me basant sur le catalogue de la db relationnelle utilisée mais cela nécessite d'avoir une db fortement normalisée pour avoir quelque chose de propre. Ca m'avait pris 1/2 journée pour avoir qqch de fonctionnelle donc rien d'insurmontable
    Justement le but est de ne pas utiliser EF, que je connais mal, mais que beaucoup de développeurs n'aiment pas, sûrement pour de bonnes raisons.

    Du coup j'ai trouvé un script pour créer les DTO à partir de la base de données.

    Code SQL :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
    DECLARE @Schema VARCHAR(MAX) = N'dbo'
    DECLARE @TableName VARCHAR(MAX) = N'ENT'
    DECLARE @Namespace VARCHAR(MAX) = N'MonApplication.DTO'
    DECLARE @Chemin VARCHAR(MAX) = 'C:\MonApplication\' + @TableName + '.cs'
     
    DECLARE @CRLF VARCHAR(2) = CHAR(13) + CHAR(10);
    DECLARE @result VARCHAR(max) = ' '
    DECLARE @constructor VARCHAR(max) = ' '
     
    DECLARE @PrivateProp VARCHAR(100) = @CRLF + 
    				--'		/// <summary>' + @CRLF +
    				--'		/// ' + @CRLF + 
    				--'		/// </summary>' + @CRLF +
                    CHAR(9) + CHAR(9) + 'public <ColumnType> <ColumnName> { get; set; }' + @CRLF;
    DECLARE @LigneAffectation VARCHAR(100) = '		<ColumnName> = CommonBase.<ColumnType>;' + @CRLF; 
     
    SET @result = 'using System;' + @CRLF + @CRLF +
    				'using MonApplication.COMMON;' + @CRLF + @CRLF +
                    'namespace ' + @Namespace  + @CRLF + '{' + @CRLF +
                    --'	/// <summary>' + @CRLF +
    				--'	/// ' + @CRLF + 
    				--'	/// </summary>' + @CRLF +
                    '	public class ' + @TableName + ' : DTOBase' + @CRLF + 
                    '	{' + @CRLF +
                    '		#region Instance Properties' + @CRLF 
     
    SET @constructor =	'///<summary>' + @CRLF +
    					'	/// Constructeur' + @CRLF +
    					'	/// Aucun paramètre et tous les types sont initialisés à leur' + @CRLF +
    					'	/// valeurs nulles telles que définies dans CommonBase.' + @CRLF +
    					'	///</summary>' + @CRLF + 
    					'	public ' + @TableName + ' ()' + @CRLF + 
    					'	{' + @CRLF  
     
    SELECT @constructor = @constructor
                     + 
                    REPLACE(
                                REPLACE(@LigneAffectation
                                , '<ColumnName>', ColumnNameC)
                            , '<ColumnType>', ColumnTypeC)
     
    FROM
    (
        SELECT  cc.COLUMN_NAME   AS ColumnNameC 
            , CASE cc.DATA_TYPE   
                WHEN 'bigint' THEN 'Int_NullValue'
                WHEN 'binary' THEN 'Int_NullValue'
                WHEN 'bit' THEN 'Int_NullValue'             
                WHEN 'char' THEN 'String_NullValue'
                WHEN 'date' THEN 'DateTime_NullValue'                         
                WHEN 'datetime' THEN 'DateTime_NullValue'                         
                WHEN 'datetime2' THEN 'DateTime_NullValue'                         
                WHEN 'datetimeoffset' THEN 'DateTimeOffSet_NullValue'                                    
                WHEN 'decimal' THEN 'Decimal_NullValue'                                    
                WHEN 'float' THEN 'Float_NullValue'                                    
                WHEN 'image' THEN 'ByteTab_NullValue'
                WHEN 'int' THEN 'Int_NullValue'
                WHEN 'money' THEN 'Decimal_NullValue'                                                
                WHEN 'nchar' THEN 'String_NullValue'
                WHEN 'ntext' THEN 'String_NullValue'
                WHEN 'numeric' THEN 'Decimal_NullValue'                                                            
                WHEN 'nvarchar' THEN 'String_NullValue'
                WHEN 'real' THEN 'Double_NullValue'                                                                         
                WHEN 'smalldatetime' THEN 'DateTime_NullValue'                                    
                WHEN 'smallint' THEN 'Int_NullValue'            
                WHEN 'smallmoney' THEN 'Decimal_NullValue'                                                                        
                WHEN 'text' THEN 'String_NullValue'
                WHEN 'time' THEN 'TimeSpan_NullValue'                                                                                    
                WHEN 'timestamp' THEN 'DateTime_NullValue'                                    
                WHEN 'tinyint' THEN 'Byte_NullValue'                                                
                WHEN 'uniqueidentifier' THEN 'Guid_NullValue'
                WHEN 'varbinary' THEN 'ByteTab_NullValue'
                WHEN 'varchar' THEN 'String_NullValue'
                ELSE 'Object'
            END AS ColumnTypeC
            , cc.ORDINAL_POSITION 
    FROM    INFORMATION_SCHEMA.COLUMNS cc
    WHERE   cc.TABLE_NAME = @TableName 
        AND ISNULL(@Schema, cc.TABLE_SCHEMA) = cc.TABLE_SCHEMA  
    ) tc
    ORDER BY tc.ORDINAL_POSITION
     
     
     
    SELECT @result = @result
                     + 
                    REPLACE(
                                REPLACE(@PrivateProp
                                , '<ColumnName>', ColumnName)
                            , '<ColumnType>', ColumnType)
     
    FROM
    (
        SELECT  c.COLUMN_NAME   AS ColumnName 
            , CASE c.DATA_TYPE   
                WHEN 'bigint' THEN 'Int64'
                WHEN 'binary' THEN 'Byte[]'
                WHEN 'bit' THEN 'Boolean'             
                WHEN 'char' THEN 'String'
                WHEN 'date' THEN 'DateTime'                         
                WHEN 'datetime' THEN 'DateTime'                         
                WHEN 'datetime2' THEN 'DateTime'                         
                WHEN 'datetimeoffset' THEN 'DateTimeOffset'                                    
                WHEN 'decimal' THEN 'Decimal'                                    
                WHEN 'float' THEN 'Single'                                    
                WHEN 'image' THEN 'Byte[]'
                WHEN 'int' THEN 'Int32'
                WHEN 'money' THEN 'Decimal'                                                
                WHEN 'nchar' THEN 'String'
                WHEN 'ntext' THEN 'String'
                WHEN 'numeric' THEN 'Decimal'                                                            
                WHEN 'nvarchar' THEN 'String'
                WHEN 'real' THEN 'Double'                                                                         
                WHEN 'smalldatetime' THEN 'DateTime'                                    
                WHEN 'smallint' THEN 'Int16'            
                WHEN 'smallmoney' THEN 'Decimal'                                                                        
                WHEN 'text' THEN 'String'
                WHEN 'time' THEN 'TimeSpan'                                                                                    
                WHEN 'timestamp' THEN 'DateTime'                                    
                WHEN 'tinyint' THEN 'Byte'                                                
                WHEN 'uniqueidentifier' THEN 'Guid'
                WHEN 'varbinary' THEN 'Byte[]'
                WHEN 'varchar' THEN 'String'
                ELSE 'Object'
            END AS ColumnType
            , c.ORDINAL_POSITION 
    FROM    INFORMATION_SCHEMA.COLUMNS c
    WHERE   c.TABLE_NAME = @TableName 
        AND ISNULL(@Schema, c.TABLE_SCHEMA) = c.TABLE_SCHEMA  
    ) t
    ORDER BY t.ORDINAL_POSITION
     
    SELECT @result = @result + @CRLF + 
                    CHAR(9) + '	#endregion Instance Properties' + @CRLF +
                    CHAR(9) + @constructor + @CRLF +
                    CHAR(9) + '}' + @CRLF +
                    CHAR(9) + '}' + @CRLF +
                    @CRLF + '}' 
    --SELECT @result
     
    EXEC USP_SaveFile @Result,@Chemin 
     
    print CAST(@Result AS TEXT)



    Maintenant si quelqu'un en a un plus complet pour générer les classes des couches supérieures décrites dans l'article, ça m'intéresse au plus haut point
      0  0

  20. #20
    Modérateur

    Je n'ai rien de tout fait, mais tu peux aussi regarder du cote des templates T4. Ca permet de coder (en C# / VB.NET) la generation de tes classes : Code Generation and T4 Text Templates.
    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.